
	/* Hax for javascript shortcomings */
	function inArray (arr, value) {
		var i;
		for (i=0; i < arr.length; i++) {
			if (arr[i] === value) {
				return true;
			}
		}
		return false;
	};

	YAHOO.namespace('Infinite');

	YAHOO.Infinite.CategoryPicker = function(containerEl, listenersJson, formUri, widget) {
		this.categoryContainer = YAHOO.util.Dom.get(containerEl);

		if (this.categoryContainer.nodeName == 'FORM') {
			this.categoryForm = this.categoryContainer;
		}
		else {
			this.categoryForm = YAHOO.util.Dom.getAncestorByTagName(this.categoryContainer, 'form');
		}

		this.formUri = formUri;

		if (!YAHOO.lang.isUndefined(widget)) {
			this.widget = widget;
		}

		try {
			this.listeners = YAHOO.lang.JSON.parse(listenersJson);
			this.attachListeners();
		}
		catch (e)
		{ /* */ }
	}

	YAHOO.Infinite.CategoryPicker.prototype = {
		categoryContainer: null,
		categoryForm: null,
		listeners: null,
		formUri: null,
		widget: false,

		ajaxFire: {},

		attachListeners: function (changers) {
			for (i = 0, j = this.listeners.length; i < j; i++)
			{
				listenerContainer = YAHOO.util.Dom.get(this.listeners[i].listener);
				listenerInputs = YAHOO.util.Selector.query('input', listenerContainer);
				listenerSelects = YAHOO.util.Selector.query('select', listenerContainer);

				for (k = 0, m = listenerInputs.length; k < m; k++)
				{
					if (!YAHOO.lang.isUndefined(changers) && !inArray(changers, listenerInputs[k])) { continue; }
					YAHOO.util.Event.addListener(listenerInputs[k], 'click', this.handleClick, this.listeners[i], this);
				}

				for (k = 0, m = listenerSelects.length; k < m; k++)
				{
					if (!YAHOO.lang.isUndefined(changers) && !inArray(changers, listenerSelects[k])) { continue; }
					YAHOO.util.Event.addListener(listenerSelects[k], 'change', this.handleClick, this.listeners[i], this);
				}
			}
		},

		handleClick: function (e, listener) {
			// handle already ajaxing for specific listeners
			if (!YAHOO.lang.isUndefined(this.ajaxFire[listener.listener]))
			{
				YAHOO.util.Connect.abort(this.ajaxFire[listener.listener]);
			}

			this.removeEntries(listener.changers);
			this.fireAjax(listener.listener, listener.changers);
		},

		removeEntries: function (changers) {
			for (i = 0, j = changers.length; i < j; i++)
			{
				choicediv = YAHOO.util.Selector.query('.choices', changers[i], true);
				if (choicediv) choicediv.innerHTML = '<img src="/images/ajax-loading.gif" />';
			}
		},

		// Copied from connection.js from YUI to replicate behaviour of setForm on specific elements
		getCatEls: function() {
			els = this.categoryForm.elements;

			var data = [];

			for (i = 0, ilen = els.length; i < ilen; i++)
			{
				if (!YAHOO.lang.isUndefined(els[i].name) && els[i].name.indexOf('cat_') >= 0)
				{
					el = els[i];

					if (!el.disabled && el.name)
					{
						name = encodeURIComponent(el.name) + '=';
						value = encodeURIComponent(el.value);

						switch(el.type)
						{
							case 'select-one':
								if (el.selectedIndex > -1) {
									opt = el.options[el.selectedIndex];
									data[data.length] = name + encodeURIComponent(
										(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
								}
								break;
							case 'select-multiple':
								if (el.selectedIndex > -1) {
									for(j=el.selectedIndex, jlen=el.options.length; j<jlen; ++j){
										opt = el.options[j];
										if (opt.selected) {
											data[data.length] = name + encodeURIComponent(
												(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
										}
									}
								}
								break;
							case 'radio':
							case 'checkbox':
								if(el.checked){
									data[data.length] = name + value;
								}
								break;
							case 'file':
								// stub case as XMLHttpRequest will only send the file path as a string.
							case undefined:
								// stub case for fieldset element which returns undefined.
							case 'reset':
								// stub case for input type reset button.
							case 'button':
								// stub case for input type button elements.
								break;
							case 'submit':
								/* we dont want a submit button if(hasSubmit === false){
									if(this._hasSubmitListener && this._submitElementValue){
										data[item++] = this._submitElementValue;
									}
									else{
										data[item++] = oName + oValue;
									}

									hasSubmit = true;
								} */
								break;
							default:
								data[data.length] = name + value;
						}
					}
				}
			}

			return data.join('&');
		},

		fireAjax: function (listener, changers)
		{
			var callback = {
				success: function (responseObj) {
					try {
						response = YAHOO.lang.JSON.parse(responseObj.responseText);
					}
					catch (e)
					{ /* TODO: gracefully handle errors */ }

					if (response.success)
					{
						for (i = 0, j = response.elements.length; i < j; i++)
						{
							YAHOO.util.Dom.get(response.elements[i].changer).innerHTML = response.elements[i].element;
						}

						responseObj.argument.obj.attachListeners(responseObj.argument.changers);
						delete responseObj.argument.obj.ajaxFire[listener];
					}
					else
					{ /* TODO: gracefully handle errors */ }
				},

				failure: function (responseObj) {
					/* TODO: gracefully handle errors */
					// what to do when it fails? redirect back to the search page with a get string of the selections?
				},

				argument: {
					obj: this,
					changers: changers,
					listener: listener
				}
			};

			elvals = this.getCatEls();
			if (this.widget) { widval = "&xhrwidget=1" } else { widval = "" }
			this.ajaxFire[listener] = YAHOO.util.Connect.asyncRequest('GET', this.formUri + YAHOO.lang.JSON.stringify(changers) + '&' + elvals + widval, callback);
		}
	}

