/**
 * DynamicSelect.js
 * 
 * An object that turns standard selectboxes into dynamic selectboxes.
 * 
 * Dynamic selectboxes have open the possibility to type a text into a text
 * inputbox and thus restricting the number of options in the selectbox.
 * 
 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
 * @version 1.00, 11/13/2007
 * @package CMS
 */
function DynamicSelect() {

	/**
	 * Reference to the current scope.
	 * @var object self
	 * @access private
	 */
	var self = this;
	
	/**
	 * Initialization method.
	 * 
	 * Turns all selectboxes in the current-document into dynamic selectboxes.
	 * 
	 * @return	boolean		true on success, false on failure.
	 * @access	privileged
	 */
	this.init = function() {
		addEvent(document, 'click', _closeDynSelects);
		return this.activateDynamicSelects();
	} // init()
	
	/**
	 * A method to activate all select elements that were added with the 'dyn'
	 * attribute set to 'yes'.
	 * 
	 * @return	boolean			true on success, false on failure.
	 * @access	privileged
	 */
	this.activateDynamicSelects = function() {
		var selects,		// all select elements in curr. doc. with dyn='yes' attr.
				dyn,				// dynamically created dynamic selectbox.
				sel,				// replaced selectbox.
				i;					// iterator.

		selects = document.getElementsByAttribute('dyn', 'yes', 'select');
		for ( i = 0; i < selects.length; i++ ) {
			if ( selects[i].parentNode.getAttribute('name') == selects[i].getAttribute('name') ) {
				continue;
			}
			if ( !(dyn = _createDynamic(selects[i])) ) continue;
			selects[i].style.display = 'none';
			sel = selects[i].parentNode.replaceChild(dyn, selects[i]);
			dyn.appendChild(sel);
		}
		
		return true;
	} // this.activateDynamicSelects()
	
	/**
	 * Create a dynamic select that should replace standard select element.
	 * 
	 * @param		object		select		select element to create replacement for.
	 * @return	object		dyn				replacement element.
	 * @access	private
	 */
	function _createDynamic(select) {
		var dyn,			// dynamic select element.
				options,	// element containing the select options.
				opt,			// element that represents an option in the dyn container.
				arrow,		// arrow in select to open the dropdown.
				input,		// text input in first list element.
				hidden,		// hidden input to save the value of the selectbox.
				txt,			// caption of opt.
				val,			// value of opt.
				i;				// iterator.
				
		dyn = document.createElement('span');
		dyn.className = 'dynselect';
		dyn.setAttribute('name', select.name);
		dyn.setAttribute('dyn', 'yes');
		dyn.id = select.name;
		
		// add arrow at the right hand-side of the dynamic select.
		arrow = document.createElement('span');
		arrow.className = 'arrow';
		dyn.appendChild(arrow);
		
		// add text input to dynselect.
		input = document.createElement('input');
		input.type = 'text';
		input.name = select.name + '_txtinput';
		addEvent(input, 'keydown', _handleKeyDown);
		addEvent(input, 'keyup', _handleKeyUp);
		
		dyn.appendChild(input);
		
		// create div that should contain select options.
		options = document.createElement('div');
		options.className = 'dynoptions';
		dyn.appendChild(options);
		
		for ( i = 0; i < select.options.length; i++ ) {
			opt = document.createElement('a');
			opt.href = '';
			opt.className = 'dynopt';
			
			txt = document.createTextNode(select.options[i].text);
			val = select.options[i].value;
			sel = val == select.options[select.selectedIndex].value ? 'yes' : '';
			if ( sel ) input.value = select.options[i].text;
			
			opt.setAttribute('val', val);
			opt.appendChild(txt);
			opt.setAttribute('selected', sel);
			options.appendChild(opt);
		}
		
		addEvent(dyn, 'click', _handleClick);
		return dyn;
	} // _createDynamic()
	
	/**
	 * Get the <span> element with attribute 'dyn' set to 'yes' from the given
	 * element.
	 * 
	 * @param		object		el		element to get dyn from.
	 * @return	mixed						dynamic select element found or false on failure.
	 * @access	private
	 */
	function _getDynFromEventSrc(el) {
		el = getParentNode('span', el);
		while ( el && !el.getAttribute('dyn') ) {
			el = getParentNode('span', el.parentNode);
		}
		if ( !el || !el.getAttribute || !el.getAttribute('dyn') ) return false;
		return el;
	} // _getDynFromEventSrc()
	
	/**
	 * Get the <div> element with the options from the given element.
	 * 
	 * @param		object		el		element to get options div from.
	 * @return	mixed						dynamic select element found or false on failure.
	 * @access	private
	 */
	function _getOptionsFromEventSrc(el) {
		if ( !el.getAttribute ) return false;
		
		if ( !el.getAttribute('dyn') ) {
			if ( !(el = _getDynFromEventSrc(el)) ) return false;
		}
		options = el.getElementsByTagName('div');
		if ( !options.length ) return false;
		return options[0];
	} // _getOptionsFromEventSrc()
	
	/**
	 * Handles click on dynamic select element.
	 * 
	 * @param		object		[e]		event object for Mozilla based browsers.
	 * @return	void
	 * @access	private
	 */
	function _handleClick(e) {
		var eventSrc,		// event source.
				dyn,				// dynamic select (<div>) element.
				options,		// element containing options.
				inputs,			// inputs in dyn element.
				input,			// text input dyn element.
				hidden,			// hidden input in dyn element.
				i;					// iterator.
		
		if ( !e ) e = window.event;
		eventSrc = getEventSrc(e);
		if ( eventSrc.tagName.toLowerCase() == 'input' ) {
			return _handleInputClick(e, eventSrc);
		}
		
		if ( !(dyn = _getDynFromEventSrc(eventSrc)) ) return false;
		if ( !(options = _getOptionsFromEventSrc(eventSrc)) ) return false;
		
		options.className = options.className != 'open' ? 'open' : '';
		_resetAllOptions(options);
		
		inputs = dyn.getElementsByTagName('input');
		if ( inputs.length ) input = inputs[0];
		
		var select = dyn.getElementsByTagName('select')[0];

		if ( eventSrc.tagName.toLowerCase() == 'a' ) {
			input.value = eventSrc.innerHTML;
			select.value = eventSrc.getAttribute('val');
			triggerEvent(select, 'change');
		}
		
		if ( e.preventDefault ) e.preventDefault();
		return false;
	} // _handleClick()
	
	/**
	 * Handles click on input element within dynamic select element.
	 * 
	 * @param		object		e				event object.
	 * @param		object		input		input that was clicked.
	 * @return	void
	 * @access	private
	 */
	function _handleInputClick(e, input) {
		input.focus();
		input.select();
	
		if ( e.preventDefault ) e.preventDefault();
		return false;
	} // _handleInputClick()
	
	/**
	 * Handle key-down on the input element.
	 * 
	 * @param		object		e		event source for compliant browsers.
	 * @return	boolean				true for all keys but <return>
	 * @access	private
	 */
	function _handleKeyDown(e) {
		var key;					// keycode of pressed key.
		
		if ( !e ) e = window.event;
		key = e.keyCode && e.keyCode > 0 ? e.keyCode : e.which;

		switch ( key ) {
			// "ENTER" pressed, paste a <br> in the selection.
			case 13: {
				if ( e.preventDefault ) e.preventDefault();
				_handleKeyUp(e);
				return false;
			}
		}
		
		return true;
	} // _handleKeyDown()
	
	/**
	 * Handles the keydown event on the text input in the dynamic select.
	 * 
	 * @param		object		[e]		event object for Mozilla based browsers.
	 * @return	void
	 * @access	private
	 */
	function _handleKeyUp(e) {
		var eventSrc,			// event source.
				dyn,					// dynamic select element.
				options,			// options <div> element.
				opt,					// option (<li>) element in dynamic select.
				optval,				// value of opt.
				val,					// value of the input.
				len,					// string length of val.
				i;						// iterator.
		
		if ( !e ) e = window.event;
		eventSrc = getEventSrc(e);
		
		if ( eventSrc.tagName.toLowerCase() != 'input' ) {
			return false;
		}
		
		dyn = eventSrc.parentNode;
		options = dyn.getElementsByTagName('div')[0];
		
		options.className = 'open';
		val = eventSrc.value;
		
		// reset value of matching (hidden) <select> element.
		dyn.getElementsByTagName('select')[0].value = '';
		
		// Start with 1, the first option ('all') should always be shown.
		for ( i = 1; i < options.childNodes.length; i++ ) {
			if ( options.childNodes[i].firstChild.nodeValue.toLowerCase().indexOf(val.toLowerCase()) != -1 ) {
				options.childNodes[i].style.display = 'block';
			} else {
				options.childNodes[i].style.display = 'none';
			}
		}
		
		return true;
	} // _handleKeyDown()
	
	/**
	 * Close all dynamic selectboxes.
	 * 
	 * @return	void
	 * @access	private
	 */
	function _closeDynSelects(e) {
		var eventSrc,		// event source.
				selects,		// all dynamic selects in document.
				options,		// element with select options.
				i;					// iterator.
				
		if ( !e ) e = window.event;
		eventSrc = getEventSrc(e);
		if ( dyn = _getDynFromEventSrc(eventSrc) ) return false;
		
		selects = document.getElementsByAttribute('dyn', 'yes', 'span');
		for ( i = 0; i < selects.length; i++ ) {
			options = _getOptionsFromEventSrc(selects[i]);
			options.className = '';
			_resetAllOptions(selects[i]);
		}
	} // _closeDynSelect()
	
	/**
	 * Reset the 'display' style setting for all options in given dynamic select
	 * element.
	 * 
	 * @param		object		dyn		dynamic select <ul> element.
	 * @return	boolean					true on success, false on failure.
	 * @access	private
	 */
	function _resetAllOptions(dyn) {
		var opts,	// <li> elements in given dyn.
				i;		// iterator.
		
		if ( !dyn || !dyn.tagName || dyn.tagName.toLowerCase() != 'span' ) return false;
		opts = dyn.getElementsByTagName('a');
		
		for ( i = 0; i < opts.length; i++ ) {
			opts[i].style.display = '';
		}
		
		return true;
	} // _resetAllOptions()

} // end of DynamicSelect()
