/* Centova Webmail
 * Copyright 2005, eSite Media Inc.
 *
 * Input Field extensions; provides text and caret manipulation features
 * and autocomplete support for input fields.
 */
function InputField(name) {
	this.fields = new Array();
	this.name = name;

	// is the suggestion (AutoComplete) box visible?
	this.suggest_visible = false;
	// if the suggestion box is visible, this value corresponds to the field to which it is attached
	this.suggest_field = null;
	// true if the suggestion box has focus, false if the input field has focus, otherwise undefined
	this.suggest_focused = false;
	// internal use
	this.suggest_hide_timer = null;

	// corresponds to the current InputField-registered field that has focus, or null if no
	// InputField-registered field is focused
	this.focused_field = null;

	
	this.initialize_suggest();
	
	return this;
}

// Registers InputField functionality with the specified <INPUT> field
InputField.prototype.register_field = function(input) {
	input.set_selection_range = this.set_selection_range;
	input.move_caret_to_end = this.move_caret_to_end;
	input.move_caret_to_begin = this.move_caret_to_begin;
	input.move_caret_to_pos = this.move_caret_to_pos;
	input.replace_selection = this.replace_selection;
	input.store_caret = this.store_caret;
	input.simulate_key_event = this.simulate_key_event;

	input.ifo = this;

	if (input.createTextRange) {
		input.old_onselect = input.onselect;
		input.onselect = this.handle_onselect;

		input.old_onclick = input.onclick;
		input.onclick = this.handle_onclick;
	}
	
	input.old_onkeyup = input.onkeyup;
	input.onkeyup = this.handle_onkeyup;
	
	input.old_onfocus = input.onfocus;
	input.onfocus = this.handle_onfocus;
	
	input.old_onblur = input.onblur;
	input.onblur = this.handle_onblur;
	
	// require a valid minimum text length for suggestions
	if (!input.suggestminlength) input.suggestminlength = 3;
	// require a valid delay before the suggestion box appears
	if (!input.suggestwait) input.suggestwait = 500;
	
	
	this.fields[this.fields.length] = input;
}

// Handles field onselect events (used for IE caret management)
InputField.prototype.handle_onselect = function(e) {
	this.store_caret();
	if (this.old_onselect) this.old_onselect(e);
}

// Handles field onclick events (used for IE caret management)
InputField.prototype.handle_onclick = function(e) {
	this.store_caret();
	if (this.old_onclick) this.old_onclick(e);
}

// Handles field keyup events; used for suggestion system
InputField.prototype.handle_onkeyup = function(e) {
	this.store_caret();
	if (this.old_onkeyup) this.old_onkeyup(e);
	
	e = browser.get_event(e);
	
	
	var field = e.target;
	
	// check for the down arrow in an input field
	if (e.keyCode==40) {
		// if the suggestion box is already visible, focus it
		if (this.ifo.suggest_visible) {
			this.ifo.suggest.options[0].selected = true;
			this.ifo.suggest.focus();
		
		// otherwise, bring it up if the text is long enough
		} else if (field.value.length>=field.suggestminlength) {
			this.ifo.suggest_timer_field = field;
			this.ifo.fire_onsuggest();
		}
		
		if (this.ondownarrow) this.ondownarrow();
		return;
	}
	
	// if the user recently pressed a keystroke and the onsuggest()-firing timer is
	// set, clear it
	if (this.ifo.suggest_timer != null) {
		clearTimeout(this.ifo.suggest_timer);
		this.ifo.suggest_timer = null;
	}
	
	// if suggestion support is enabled (indicated by the <INPUT> field having an
	// "onsuggest" property set to a valid function)
	if (typeof field.onsuggest == 'function') {

		// if the field text length is greater than the minimum suggestion length, process it
		if (field.value.length>=field.suggestminlength) {
			
			// fire the onsuggeststart method if available
			if (!this.ifo.suggest_visible && (typeof field.onsuggeststart == 'function') ) field.onsuggeststart(field);
			
			// fire the onsuggest method to obtain a list of suggestions; the field's onsuggest
			// method should callback the InputField class' set_suggestion() method to provide
			// its list of suggestions asynchronously
			//
			// a timer is used to delay the execution of the onsuggest() method to avoid hammering
			// the server with HTTP requests every time the user hits a flurry of keystrokes
			this.ifo.suggest_timer_field = field;
			//alert(this.ifo.name+'.fire_onsuggest()');
			this.ifo.suggest_timer = setTimeout(this.ifo.name+'.fire_onsuggest()',field.suggestwait);
			
		// if the field text length is less than the minimum suggestion length...
		} else {
			// ...and the suggestion window is visible...
			if (this.ifo.suggest_visible) {
				// ...then fire the onsuggestdone handler (if available) and hide the suggestion window
				if (typeof field.onsuggestdone == 'function') field.onsuggestdone(field);
				this.ifo.suggest_hide();
			}
		}
	}
	
}

// Called via timer to fire the onsuggest() method for a particular field
InputField.prototype.fire_onsuggest = function() {
	if (this.suggest_timer_field) this.suggest_timer_field.onsuggest(this.suggest_timer_field);
	this.suggest_timer = null;
}

// Called by third-party code in response to an onsuggest() event; provides the
// list of suggestions to show in the suggestion box
InputField.prototype.set_suggestion = function(field,values) {
	// discard the suggestion list if the user changed fields before the
	// callback was invoked
//	alert(this.suggest_field);
//	alert('bachi: '+field.id+' / '+this.suggest_field.id);
	if ( (field == null) || (field != this.focused_field) ) return;
	
	// if there were any suggestions...
	if (values && values.length>0) {
		// show them
		this.suggest_show(field,values);
		
		var fieldlen = field.value.length;
		field.value = values[0];
		field.set_selection_range(fieldlen,values[0].length);

	// otherwise...
	} else if (this.suggest_visible) {
		// fire the onsuggestdone method if provided
		if (typeof field.onsuggestdone == 'function') field.onsuggestdone(field);
		// then hide the suggestion window
		this.suggest_hide();
	}
}


// Handles field focus events; used for suggestion system
InputField.prototype.handle_onfocus = function(e) {
	if (this.old_onfocus) this.old_onfocus(e);

	e = browser.get_event(e);
	
	if ( (this.ifo.suggest_field==e.target) && this.ifo.suggest_visible ) {
		this.ifo.clear_suggest_hide_timer();
	}
	
	this.ifo.focused_field = e.target;
		
	this.ifo.suggest_focused = false;
}

// Handles field blur events; used for suggestion system
InputField.prototype.handle_onblur = function(e) {
	if (this.old_onblur) this.old_onblur(e);

	this.ifo.focused_field = null;

	this.ifo.suggest_timed_hide();
}

// Sets the selection range for a field
InputField.prototype.set_selection_range = function(selectionStart, selectionEnd) {
	if (this.setSelectionRange) {
		this.focus();
		this.setSelectionRange(selectionStart, selectionEnd);
	}
	else if (this.createTextRange) {
		var range = this.createTextRange();
		range.collapse(true);
		range.moveEnd('character', selectionEnd);
		range.moveStart('character', selectionStart);
		range.select();
	}
}

// Moves the caret to the end of an input field
InputField.prototype.move_caret_to_end = function() {
 	this.set_selection_range(this.value.length, this.value.length);
}
// Moves the caret to the beginning of an input field
InputField.prototype.move_caret_to_begin = function() {
	this.set_selection_range(0, 0);
}
// Moves the caret to a given character in an input field
InputField.prototype.move_caret_to_pos = function(pos) {
	this.set_selection_range(pos, pos);
}

// Replaces the selection in an input field
InputField.prototype.replace_selection = function(replaceString) {
	if (this.setSelectionRange) {
		var selectionStart = this.selectionStart;
		var selectionEnd = this.selectionEnd;
		this.value = this.value.substring(0, selectionStart) + replaceString + this.value.substring(selectionEnd);
		if (selectionStart != selectionEnd) // has there been a selection
			this.set_selection_range(selectionStart, selectionStart + replaceString.length);
		else // set caret
			this.move_caret_to_pos(selectionStart + replaceString.length);
	}
	else if (this.createTextRange && this.caretPos) {
		var caretPos = this.caretPos;
		caretPos.text = (caretPos.text.charAt(caretPos.text.length - 1)==' '?replaceString+' ':replaceString);
		this.focus();
	} else {
		this.value += replaceString;
		this.focus();
	}
}

// Stores the caret position in an input field (required for IE)
InputField.prototype.store_caret = function() {
	if (this.createTextRange) this.caretPos = document.selection.createRange().duplicate();
}

// Initializes the suggestion (AutoComplete) system
InputField.prototype.initialize_suggest = function() {
	this.suggest = document.createElement('select');
	this.suggest.setAttribute('size','5');
	this.suggest.className = 'envelope_suggest';
	this.suggest.owner = this;
	this.suggest.onfocus = this.suggest_focus_handler;
	this.suggest.onclick = this.suggest_click_handler;
	this.suggest.onkeyup = this.suggest_keyup_handler;
	this.suggest.onkeydown = this.suggest_keydown_handler;
	this.suggest.onblur = this.suggest_blur_handler;

	document.body.appendChild(this.suggest);
}

// Shows the suggestion box for a given input field
InputField.prototype.suggest_show = function(field,values) {
	/*
	var selrow = this.selectedrow;
	var row = this.table.rows[selrow];
	if (!row) return;
	if (!row.cells || row.cells.length<2) return;
	
	var field = row.cells[1].childNodes[0];
	*/

	if (!this.suggest_visible) {
		var p = layer.position(field);
		var s = layer.size(field);
		
		this.suggest.style.top = (p.y + s.y)+'px';
		this.suggest.style.left = p.x+'px';
		// this.suggest.style.width = s.x+'px';
		//this.suggest.style.height = '16px';
	
		this.suggest_visible = true;
		this.suggest.style.display = 'block';
		
		this.suggest_field = field;
	}
	
	this.suggest.options.length = 0;
	
	for (var i=0; i<values.length; i++) {
		var opt = new Option(values[i],values[i]);
		this.suggest.options[this.suggest.options.length] = opt;
	}
	
}

// Hides the suggestion box
InputField.prototype.suggest_hide = function() {
	if (this.suggest_focused) {
		this.suggest_focused = false;
		return;
	}
	if (this.suggest_visible) this.suggest.style.display = 'none';
	this.suggest_visible = false;
}

// Called when the suggestion box is focused
InputField.prototype.suggest_focus_handler = function() {
	this.owner.suggest_focused = true;
}

// Called when the suggestion box loses focus (starts the timer to hide the
// suggestion box automatically unless the user clicked back on the input field)
InputField.prototype.suggest_blur_handler = function() {
	this.owner.suggest_focused = false;
	this.owner.suggest_timed_hide();
}

// Called when the user clicks on an item in the suggestion box
InputField.prototype.suggest_click_handler = function(e) {
	this.owner.suggest_field.value = this.options[this.selectedIndex].value;
	this.owner.suggest_hide();

	// simulate a CR to seal the deal
	this.owner.suggest_field.simulate_key_event(13);
}

// Called when the user releases a key in the suggestion box
InputField.prototype.suggest_keyup_handler = function(e) {
	e = browser.get_event(e);
	
	// on CR, simulate a click on the selected item
	if ( (e.keyCode==13) && (typeof this.onclick == 'function') ) this.onclick(e);
}

// Called when the user presses a key in the suggestion box
InputField.prototype.suggest_keydown_handler = function(e) {
	e = browser.get_event(e);
	
	// if the user presses the UP key while the first item is selected,
	// transfer focus back to the input field
	if ( (e.keyCode == 38) && (this.selectedIndex==0) ) {
		this.owner.suggest_field.focus();
		this.owner.suggest_field.move_caret_to_end();
	}
	
}

// Simulates a key event in a field.  Note that this just fires the events
// associated with the keystroke, it does not actually insert the character.
InputField.prototype.simulate_key_event = function(keycode) {
	var e = {
		target: this,
		keyCode: keycode
	};
	
	if (typeof this.onkeydown == 'function') this.onkeydown(e);
	if (typeof this.onkeyup == 'function') this.onkeyup(e);
	if (typeof this.onkeypress == 'function') this.onkeypress(e);
}

// Hides the suggestion box in 300ms (used to ensure that clicking over from
// the input field to the in the suggestion box doesn't hide the suggestion box)
InputField.prototype.suggest_timed_hide = function() {
	if (this.suggest_visible) this.suggest_hide_timer = setTimeout(this.name+'.suggest_hide()',300);
}

// Clears the suggestion-hide-timer (set by suggest_timed_hide() above)
InputField.prototype.clear_suggest_hide_timer = function() {
	if (this.suggest_hide_timer) {
		clearTimeout(this.suggest_hide_timer);
		this.suggest_hide_timer = null;
	}
}
