/* ---------------------------------------------------------------------------------------------------- */
/* =MXM.Form */
/* ---------------------------------------------------------------------------------------------------- */

if (!$defined(MXM)) var MXM = {};
MXM.Form = {};


/* -------------------------------------------------- */
/* =MXM.Form.Autocomplete */

MXM.Form.Autocomplete =  {};

MXM.Form.Autocomplete.Base = new Class({
	
	Implements: [Options, Events],
	
	options: {
		timeout: 300,
		minLength: 3,
		size: 10,
		scrollable: false,
		className: 'autocomplete',
		width: 200,
		onShow: $empty,
		onHide: $empty
	},
	
	initialize: function (target, options) {
		this.target = $(target);
		this.setOptions(options);
		$$('form').each(function (form) {
			if (form.hasChild(this.target)) this.form = form;
		}, this);
		this.build();
		
		this.bound = {
			blockFormSubmit: this.blockFormSubmit.bindWithEvent(this),
			checkFocus: this.checkFocus.bindWithEvent(this),
			clearOnResize: this.clearOnResize.bind(this)
		};
		
		this.target.set('autocomplete', 'off');
		this.target.addEvent('keydown', this.command.bind(this));
		this.target.addEvent('keyup', this.observe.bind(this));
		
		this.isOpen = false;
	},
	
	build: function () {
		this.container = new Element('div', {
			'class': this.options.className
		});
		this.element = new Element('div').setStyle('width', this.options.width).injectInside(this.container);
		this.choices = new Element('ul').injectInside(this.element);
		this.container.injectInside(document.body);
		
		this.iframeShim = new IFrameShim(this.container, {zindex: 999});
		if (this.iframeShim.isActive) {
			this.addEvent('show', this.iframeShim.show.bind(this.iframeShim));
			this.addEvent('hide', this.iframeShim.hide.bind(this.iframeShim));
		}
	},
		
	command: function (event) {
		this.isCommandKey = false;
		this.timer = $clear(this.timer);
		if (['enter', 'tab', 'esc', 'up', 'right', 'down', 'left'].contains(event.key) && this.isOpen) {
			this.isCommandKey = true;
			switch (event.key) {
			case 'enter':
				this.setChoice();
				event.stop();
				break;
			case 'tab':
				this.setChoice();
				break;
			case 'esc':
				this.clearChoices();
				break;
			case 'up':
				if (this.selectedChoice) this.selectChoice(this.selectedChoice.getPrevious() || this.choices.getLast());
				if (this.choices.hasClass('scrollable')) this.keyScroll();
				event.stop();
				break;
			case 'down':
				if (this.selectedChoice) this.selectChoice(this.selectedChoice.getNext() || this.choices.getFirst());
				if (this.choices.hasClass('scrollable')) this.keyScroll();
				event.stop();
				break;
			}
		}
	},
	
	keyScroll: function () {
		var coo = this.choices.getCoordinates();
		var size = this.choices.getSize();
		var scroll = this.choices.getScrollSize();
		var viewport = {
			top: coo.top,
			bottom: coo.top + coo.height,
			height: coo.height,
			scroll: scroll.y
		};
		var item = {
			top: this.selectedChoice.getTop(),
			height: this.selectedChoice.getSize().y + this.selectedChoice.getStyle('margin-bottom').toInt()
		};
		item.bottom = item.top + item.height
		if (item.bottom > (viewport.bottom + viewport.scroll)) {
			this.choices.scrollTo(0, item.bottom - viewport.bottom);
		} else if (item.top < (viewport.top + viewport.scroll)) {
			this.choices.scrollTo(0, item.top - viewport.top);
		}
	},
	
	observe: function (event) {	
		var tmpValue = this.target.get('value').trim();
		if (tmpValue.length < this.options.minLength) {
			this.queryValue = tmpValue;
			this.clearChoices();
		} else if (this.queryValue !== tmpValue && !this.isCommandKey) {
			this.queryValue = tmpValue;
			this.timer = $clear(this.timer);
			this.timer = this.query.delay(this.options.timeout, this);
		}
	},
		
	populateChoices: function (data) {
		this.clearChoices();
		if (data.length) {
			if (this.options.scrollable && this.options.size < data.length) {
				this.choices.addClass('scrollable');
			} else if (this.options.size < data.length) {
				data.length = this.options.size;
			}
			data.each(function (choice) {
				var listitem = new Element('li');
				var anchor = new Element('a').set('html', this.mark(choice)).injectInside(listitem);
				anchor.addEvent('mouseover', this.selectChoice.pass(listitem, this));
				anchor.addEvent('click', this.setChoice.bind(this));
				this.choices.adopt(listitem);
			}, this);
			this.selectChoice(this.choices.getFirst());
			this.showChoices();
		}
	},
	
	mark: function (txt) {
		return txt.replace(new RegExp('^(' + this.queryValue.escapeRegExp() + ')', 'i'), '<span class="mark">$1</span>');
	},
	
	clearChoices: function () {
		this.hideChoices();
		this.selectedChoice = null;
		this.choices.empty();
		if (this.choices.hasClass('scrollable')) {
			this.choices.removeClass('scrollable');
			this.choices.setStyle('height', 'auto');
		}
	},
	
	blockFormSubmit: function (event) {
		event.stop();
	},
	
	clearOnResize: function () {
		this.clearChoices();
	},
	
	checkFocus: function (event) {
		event.stop();
		if (event.target !== this.container && event.target !== this.target && !this.container.hasChild(event.target)) {
			this.clearChoices();
		}
	},
	
	setPosition: function () {
		var coo = this.target.getCoordinates();
		this.container.set('styles', {
			left: coo.left,
			top: coo.top + coo.height
		});
	},
	
	showChoices: function () {
		this.setPosition();
		this.container.setStyle('display', 'block');
		if (this.choices.hasClass('scrollable')) {
			var tmp = this.choices.getFirst();
			this.choices.setStyle('height', (tmp.getSize().y + tmp.getStyle('margin-bottom').toInt()) * this.options.size);
			this.choices.scrollTo(0, 0);
		}
		this.form.addEvent('submit', this.bound.blockFormSubmit);
		document.addEvent('click', this.bound.checkFocus);
		window.addEvent('resize', this.bound.clearOnResize);
		this.isOpen = true;
		this.fireEvent('show');
	},
	
	hideChoices: function () {
		this.container.setStyle('display', 'none');
		this.form.removeEvent('submit', this.bound.blockFormSubmit);
		document.removeEvent('click', this.bound.checkFocus);
		window.removeEvent('resize', this.bound.clearOnResize);
		this.isOpen = false;
		this.fireEvent('hide');
	},
	
	selectChoice: function (choice) {
		if (this.selectedChoice) this.selectedChoice.removeClass('isSelected');
		this.selectedChoice = choice;
		this.selectedChoice.addClass('isSelected');
	},
	
	setChoice: function () {
		if (this.selectedChoice) {
			this.target.set('value', this.selectedChoice.get('text'));
			this.hideChoices();
			//(Browser.Engine.trident) ? this.clearChoices.delay(500, this) : this.clearChoices(); // delay seems to fix freeze in IE
		}
	}
	
});


MXM.Form.Autocomplete.Local = new Class({
	
	Extends: MXM.Form.Autocomplete.Base,
	
	options: {
		timeout: 0
	},
	
	initialize: function (target, data, options) {
		this.parent(target, options);
		this.data = data;
	},
	
	query: function () {
		this.populateChoices(this.filter());
	},
	
	filter: function () {
		var regexp = new RegExp('^' + this.queryValue.escapeRegExp(), 'i');
		return this.data.filter(function (choice) {
			return regexp.test(choice);
		});
	}
	
});


MXM.Form.Autocomplete.Remote = new Class({
	
	Extends: MXM.Form.Autocomplete.Base,
	
	options: {
		method: 'post',
		params: {}
	},
	
	initialize: function (target, options) {
		this.parent(target, options);
		this.request = new Request.JSON({
			url: this.options.service,
			method: this.options.method,
			link: 'cancel',
			onSuccess: function (responseJSON, responseText) {
				this.populateChoices(responseJSON);
			}.bind(this)
		});
	},
	
	query: function () {
		var params = '';
		for (param in this.options.params) params += '&' + param + '=' + this.options.params[param];
		this.request.send(this.options.keyword + '=' + this.queryValue + params);
	}
	
});