/**
 * The model and view used to specify restrictions in the Advanced Search
 *
 * @author   Sean McCann
 * @version  0.3
 * @date     2005.09.23
 *
 * Changelog:
 * 2006-02-09: All condition and "list" term elements are now automatically created based on configuration settings (Sean McCann)
 * 2006-01-13: Fixed the IE-specific bug where the "date is between" filter was always disabled. (Sean McCann)
 * 2005-12-20: Added 'number' dropdown creation for the new operator type (Gabe Martin-Dempesy)
 */


/**
 * A restriction for the Advanced Search
 */
function SearchFilterModel ()
{
	try
	{
		this.target = '';
		this.condition = '';
		this.term = '';
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterModel");
	}
}

SearchFilterModel.prototype.__CLASS__    = 'SearchFilterModel';
SearchFilterModel.prototype.setTarget    = SearchFilterModelSetTarget;
SearchFilterModel.prototype.getTarget    = SearchFilterModelGetTarget;
SearchFilterModel.prototype.setCondition = SearchFilterModelSetCondition;
SearchFilterModel.prototype.getCondition = SearchFilterModelGetCondition;
SearchFilterModel.prototype.setTerm      = SearchFilterModelSetTerm;
SearchFilterModel.prototype.getTerm      = SearchFilterModelGetTerm;
SearchFilterModel.prototype.getMapIndex = SearchFilterModelGetMapIndex;


function SearchFilterModelSetTarget( target )
{
	try
	{
		var old_target = this.target;
		this.target = target;
		return old_target;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterModelSetTarget");
		return false;
	}
}

function SearchFilterModelGetTarget()
{
	return this.target;
}

function SearchFilterModelSetCondition( condition )
{
	try
	{
		var old_condition = this.condition;
		this.condition = condition;
		return old_condition;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterModelSetCondition");
		return false;
	}
}
function SearchFilterModelGetCondition()
{
	return this.condition;
}

function SearchFilterModelSetTerm( term )
{
	try
	{
		var old_term = this.term;
		this.term = term;
		return old_term;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterModelSetTerm");
		return false;
	}
}

function SearchFilterModelGetTerm()
{
	return this.term;
}

function SearchFilterModelGetMapIndex( target )
{
	try
	{
		var map_index;

		if (typeof(target) == 'undefined')
		{
			map_index = this.target;
		}
		else
		{
			map_index = target;
		}

		if (!map_index || typeof(adv_search['search_filter_map'][map_index]) == 'undefined')
		{
			map_index = 'default';
		}

		return map_index;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterModelGetMapIndex");
		return false;
	}

}






/**
 * The visual representation of each advanced search restriction
 *
 * @uses ListItemView
 * @uses ContentSelector
 */
function SearchFilterView( )
{
	try
	{
		// Keep this here to copy over properties
		// from the parent prototype object
		create_class_properties.call(this, SearchFilterView);

		this.view_element_id = ''; // default id
		//this.view_element = document.getElementById(this.view_element_id);

		this.target_elements = new Object();
		this.condition_elements = new Object();
		this.term_elements = new Object();

		this.active_target_element = null;
		this.active_condition_element = null;
		this.active_term_element = null;

		/**
		 * When a SearchFilter's target is changed, the condition
		 * element and term element that go along with that target
		 * are displayed.  However, sometimes, changing a condition
		 * element might also change the term that needs to be displayed,
		 * overriding the default mapping for the particular target.
		 * This object is used for storing that override information.
		 * @access private
		 */
		this.component_element_override = new Object();

		/**
		 * a list of each valid type of SearchFilter component
		 * @access private
		 */
		this.component_types = new Array();
		this.component_types.push('target');
		this.component_types.push('condition');
		this.component_types.push('term');
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", 'SearchFilterView');
	}
}

SearchFilterView.prototype = new ListItemView();
SearchFilterView.prototype.__CLASS__ = "SearchFilterView";
SearchFilterView.prototype.createView = SearchFilterViewCreateView;
SearchFilterView.prototype.setActiveElement = SearchFilterViewSetActiveElement;
SearchFilterView.prototype.getComponentElement = SearchFilterViewGetComponentElement;
SearchFilterView.prototype.getComponentElementForTarget = SearchFilterViewGetComponentElementForTarget;
SearchFilterView.prototype.targetChangeHandler = SearchFilterViewTargetChangeHandler;
SearchFilterView.prototype.conditionChangeHandler = SearchFilterViewConditionChangeHandler;
SearchFilterView.prototype.termChangeHandler = SearchFilterViewTermChangeHandler;
SearchFilterView.prototype.setIndex = SearchFilterViewSetIndex;
SearchFilterView.prototype.updateAllActiveComponentNames = SearchFilterViewUpdateAllActiveComponentNames;
SearchFilterView.prototype.updateActiveComponentName = SearchFilterViewUpdateActiveComponentName;


function SearchFilterViewCreateView ( model )
{
	try
	{
		var new_view_element;
		var input, dropdown, option;
		var strong;
		var text;
		var span, div;
		var i;
		var map_index;
		var condition_element;

		if (!model || !model.__CLASS__ || model.__CLASS__ != 'SearchFilterModel')
		{
			model = new SearchFilterModel();
			this.model = model;
		}

		new_view_element = document.createElement('div');
		new_view_element.setAttribute('class', 'search_filter');

		dropdown = select_from_array(adv_search['search_targets'], 'search_target[]', model.getTarget());
		dropdown.className = 'target';
		dropdown.onchange = this.targetChangeHandler;
		dropdown.view = this;
		this.target_elements['default'] = dropdown;
		new_view_element.appendChild(dropdown);



		// Create Search Conditions
		for (i in adv_search['search_conditions'])
		{
			this.condition_elements[i] = select_from_array(adv_search['search_conditions'][i], 'search_condition[]');
		}
		this.condition_elements['range'].onchange = SearchFilterViewDateConditionChangeHandler;

		for (i in this.condition_elements)
		{
			this.condition_elements[i].className = 'condition';
			this.condition_elements[i].disabled = 'disabled';
			this.condition_elements[i].view = this;
			new_view_element.appendChild(this.condition_elements[i]);
		}
		this.condition_elements['default'] = this.condition_elements['text'];


		//////////////////
		// Term Elements
		//////////////////

		// Default text entry
		this.term_elements['text'] = document.createElement('input');
		this.term_elements['text'].name = 'search_term[]';

		// List elements
		// Create all database list elements from adv_search['search_lists']
		for (i in adv_search['search_lists'])
		{
			dropdown = document.createElement('select');
			this.term_elements[i] = select_from_array(adv_search['search_lists'][i], 'search_term[]');
		}

		// Date between x and y
		this.term_elements['range_is_between'] = document.createElement('span');

		input = document.createElement('input');
		input.name = 'search_term[][0]';
		input.size = '8';
		input.disabled = 'disabled';
		this.term_elements['range_is_between'].appendChild(input);

		text = document.createTextNode(' and ');
		this.term_elements['range_is_between'].appendChild(text);

		input = document.createElement('input');
		input.name = 'search_term[][1]';
		input.size = '8';
		input.disabled = 'disabled';
		this.term_elements['range_is_between'].appendChild(input);

		// override the default hide/show methods for this term element
		this.term_elements['range_is_between'].hideElement = SearchFilterViewDateBetweenTermHideElement;
		this.term_elements['range_is_between'].showElement = SearchFilterViewDateBetweenTermShowElement;
		this.term_elements['range_is_between'].setName = SearchFilterViewDateBetweenTermSetName;
		this.term_elements['range_is_between'].setValue = SearchFilterViewDateBetweenTermSetValue;

		// Perform a few common operations on all terms
		for (i in this.term_elements)
		{
			this.term_elements[i].view = this;
			this.term_elements[i].className = 'term';
			this.term_elements[i].disabled = 'disabled';
			new_view_element.appendChild(this.term_elements[i]);
		}


		// ListItem Controls
		input = document.createElement('input');
		input.className = 'control add_control';
		input.setAttribute('type', 'button');
		input.setAttribute('name', 'add_filter');
		input.setAttribute('value', '+');
		input.onclick = this.addHandler;
		input.view = this;
		this.addControl = input;
		new_view_element.appendChild(input);

		input = document.createElement('input');
		input.className = 'control remove_control';
		input.setAttribute('type', 'button');
		input.setAttribute('name', 'remove_filter');
		input.setAttribute('value', '-');
		input.onclick = this.removeHandler;
		input.view = this;
		this.removeControl = input;
		new_view_element.appendChild(input);


		this.setActiveElement('target', this.target_elements['default'], model.getTarget())

		return new_view_element;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", 'SearchFilterCreateView');
		return null;
	}
}


/**
 * @access private
 */
function SearchFilterViewSetActiveElement(component_type, element, value)
{
	try
	{
		var active_element_index;
		var change_handler;

		// Check to see if the specified type of component is valid.
		// If not, exit immediately
		if (!in_array(component_type, this.component_types))
		{
			return false;
		}

		// Now, create the indeces that we will need to look up various
		// elements relating to the specified component.
		active_element_index = 'active_' + component_type + '_element';

		// Before we attempt to set the HTMLElement for the particular
		// SearchFilter component, check to make sure that the active
		// element is not the same as the new one being specified.
		// If so, we have saved ourselves some work.
		if (this[active_element_index] === element)
		{
			return false;
		}

		// Stop displaying the old active element for this component.
		if (this[active_element_index] !== null)
		{
			if (this[active_element_index].hideElement)
			{
				this[active_element_index].hideElement();
			}
			else
			{
				SearchFilterViewComponentHideElement.call(this[active_element_index]);
			}
		}

		// If a value was passed for this item, initialize it with that value
		else if (value)
		{
			if (typeof(element.setValue) == 'function')
			{
				element.setValue(value);
			}
			else
			{
				element.value = value;
			}
		}


		// Display the new active element
		if (element.showElement)
		{
			element.showElement();
		}
		else
		{
			SearchFilterViewComponentShowElement.call(element);
		}


		// Formally set the active element
		this[active_element_index] = element;

		// Set the name of the chosen element
		this.updateActiveComponentName(component_type);

		// Explicitly call the onchange handler to make
		// any necessecary changes to the other filter components
		if (element.onchange)
		{
			element.onchange();
		}

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", 'SearchFilterViewSetActiveElement');
		return false;
	}

}


function SearchFilterViewGetComponentElement( component, element_name )
{
	try
	{
		var elements;

		// Check to see if the specified type of component is valid.
		// If not, exit immediately
		if (!in_array(component, this.component_types))
		{
			return false;
		}

		elements = this[component + '_elements'];
		if (typeof(elements[element_name]) != 'undefined')
		{
			return elements[element_name];
		}

		return false;
	}
	catch(e)
	{
		error_message(e, 'SearchFilter.js', 'SearchFilterViewGetComponentElement');
		return false;
	}
}

function SearchFilterViewGetComponentElementForTarget( target_value, component )
{
	try
	{
		var component_element;
		var map_index;


		map_index = this.model.getMapIndex(target_value);

		// Something else may be overriding the default mapping for the component
		// element that goes with this target.  If that's the case, use the specified
		// element, and clear the mapping override.
		if (typeof(this.component_element_override[component]) != 'undefined')
		{
			component_element = this.component_element_override[component];
			this.component_element_override[component] = undefined;
		}
		// Otherwise, use our default mapping
		else
		{
			component_element = this.getComponentElement(component,adv_search['search_filter_map'][map_index][component]);
		}

		return component_element;
	}
	catch(e)
	{
		error_message(e, 'SearchFilter.js', 'SearchFilterViewGetComponentElementForTarget');
		return false;
	}
}

function SearchFilterViewSetIndex( index )
{
	this.index = index;
	this.updateAllActiveComponentNames();
}

function SearchFilterViewUpdateAllActiveComponentNames()
{
	try
	{
		var i;

		for (i = 0; i < this.component_types.length; i++)
		{
			this.updateActiveComponentName(this.component_types[i]);
		}
		return true;
	}
	catch(e)
	{
		error_message(e, 'SearchFilter.js', 'SearchFilterViewUpdateAllActiveComponentNames');
		return false;
	}
}

function SearchFilterViewUpdateActiveComponentName(component)
{
	try
	{
		var element_name = 'search_' + component + '[' + this.index + ']';
		var element = this['active_' + component + '_element'];

		if (element === null)
		{
			return false;
		}

		if (typeof(element.setName) == 'function')
		{
			element.setName();
		}
		else
		{
			element.name = element_name;
		}

		return true;
	}
	catch(e)
	{
		error_message(e, 'SearchFilter.js', 'SearchFilterViewUpdateActiveComponentName');
		return false;
	}
}

function SearchFilterViewTargetChangeHandler ()
{
	try
	{
		// Executed within the context of the <select> element.

		var condition_element;
		var condition_value;
		var term_element;
		var term_value;
		var condition_was_changed;
		var term_was_changed;

		condition_element = this.view.getComponentElementForTarget(this.value, 'condition');
		if (this.view.active_condition_element === null)
		{
			condition_value = this.view.model.getCondition();
		}
		condition_was_changed = this.view.setActiveElement('condition', condition_element, condition_value);
		// Sometimes, changing a particular condition value may change the term element being displayed.
		// Because of that, if the term element changed but the condition element did not, we still need
		// to invoke the condition element's onchange handler since it may need to change the term element
		// to something else.
		if (!condition_was_changed && typeof(this.view.active_condition_element.onchange) == 'function')
		{
			this.view.active_condition_element.onchange();
		}


		term_element = this.view.getComponentElementForTarget(this.value, 'term');
		if (this.view.active_term_element === null)
		{
			term_value = this.view.model.getTerm();
		}
		term_was_changed = this.view.setActiveElement('term', term_element, term_value);


		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewTargetChangeHandler");
		return false;
	}
}


function SearchFilterViewConditionChangeHandler ()
{
	try
	{
		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewConditionChangeHandler");
		return false;
	}
}


function SearchFilterViewTermChangeHandler ()
{
	try
	{
		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewTermChangeHandler");
		return false;
	}
}

function SearchFilterViewComponentHideElement ()
{
	try
	{
		this.style.display = 'none';
		this.setAttribute('disabled', 'disabled');

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewComponentHideElement");
		return false;
	}
}

function SearchFilterViewComponentShowElement ()
{
	try
	{
		this.style.display = 'inline';
		this.removeAttribute('disabled');

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewComponentShowElement");
		return false;
	}
}



function SearchFilterViewDateBetweenTermHideElement ()
{
	try
	{
		var inputs;
		var i;

		inputs = this.getElementsByTagName('input');
		for (i = 0; i < inputs.length; i++)
		{
			inputs[i].setAttribute('disabled', 'disabled');
		}

		this.style.display = 'none';

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewDateBetweenTermHideElement");
		return false;
	}
}

function SearchFilterViewDateBetweenTermShowElement ()
{
	try
	{
		var inputs;
		var i;

		// By default, each TermElement is disabled.  In this particular case,
		// the TermElement is a <span> that contains multiple <input> tags.
		// Even though this handler disables each of the <input> tags contained
		// by the <span>, IE6 recognizes the 'disabled' attribute on the <span>
		// and refuses to submit the values in these <input> tags as a result of
		// the disabled parent.  Therefore, we must first remove the disabled
		// attribute from the <span> before proceeding.
		this.removeAttribute('disabled');

		// manually enable all child <input> elements.
		inputs = this.getElementsByTagName('input');

		for (i = 0; i < inputs.length; i++)
		{
			inputs[i].removeAttribute('disabled');
		}

		// Display it all
		this.style.display = 'inline';

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewDateBetweenTermShowElement");
		return false;
	}
}

function SearchFilterViewDateBetweenTermSetName()
{
	try
	{
		var inputs;
		var i;

		inputs = this.getElementsByTagName('input');

		for (i = 0; i < inputs.length; i++)
		{
			inputs[i].name = 'search_term[' + this.view.index + '][' + i + ']';
		}

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewDateBetweenTermSetName");
		return false;
	}
}

function SearchFilterViewDateBetweenTermSetValue( value )
{
	try
	{
		var inputs;
		var i;

		inputs = this.getElementsByTagName('input');

		for (i = 0; i < inputs.length; i++)
		{
			inputs[i].value = value[i];
		}

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewDateBetweenTermSetValue");
		return false;
	}
}


function SearchFilterViewDateConditionChangeHandler ()
{
	try
	{
		var element;
		var value;
		// If we are using the "date" condition set, there are some special
		// things that we need to do when selecting "date is between"
		if (this.value == 'range_is_between')
		{
			element = this.view.getComponentElement('term', 'range_is_between');
			this.view.component_element_override['term'] = this.view.getComponentElement('term', 'range_is_between');
		}
		else
		{
			element = this.view.getComponentElement('term', 'text');
		}

		// Since this change handler will set the term element, initialize
		// it to whatever the model specifies if need be.
		if (this.view.active_term_element === null)
		{
			value = this.view.model.getTerm();
		}
		this.view.setActiveElement('term', element, value);

		return true;
	}
	catch(e)
	{
		error_message(e, "SearchFilter.js", "SearchFilterViewDateConditionChangeHandler");
		return false;
	}
}