/** 
 * @(#) ajax_suggestion.js
 * 
 * @version 	v0.3  Nov 12, 2009
 * @author 	    Yoon YongHyun(jazzvm, jazzvm@gmail.com)
 * Copyright (c) 2002 Jazz Virtual Machine, Inc. (Seoul, Korea.)
 * http://www.jazzvm.net All rights reserved.
 *
 * Use of this class is limited by followings;
 * 1. The name of the author can not be removed.
 * 2. In case that the author info is incorporated, 
 *       there is no limitation of the modification or redistribution of this class.
 * 3. It is not required to inform the author of the modification.
 * 
 * NOTE
 * 	- Most lines of javascript code in this package largely depend on jquery standard library. 
 *    So please include jquery 1.3.x or higher version before including this package.
 *    However, there is no need of other extensions of jquery plugins.
 * 
 */	


/*
 * [usage]
 
[[ in HTML ]]
 	<!-- JS IMPORT -->
		<script type="text/javascript" src="/js/jquery-1.3.2.min.js"></script>
		<script type="text/javascript" src="/js/ajax_suggestion.js"></script>

 	<!-- REQUIRED FUNCTIONS DECARALATION -->
		<script type="text/javascript">

			// corresponding to each json item object
			function compose_li_tag(item_object) {
				var li_tag  = $("<li>" + item_object.title + "</li>");
				var pid = item_object.seq;
				li_tag.attr('pid', pid);
				return li_tag;
			}
			
			// fired by entering or clicking a list item.
			function after_selection(list_tag) {
				location.href = "/board/view.jazz?seq=" + $(list_tag).attr('pid');
			}
			
			// ajax invoke function
			function invoke_ajax(the_object) {
				$.ajax({
				    url: '/some_json_processing.url',
				    type: 'GET',
				    dataType: 'json',
					data: { some_other_attributes: some_other_attributes, ... },
					// do not modify following call back methods
				    error: function(){
						the_object.init_variables();
				    },
				    success: function(response) {			    
				    	the_object.render_result(response, the_object);
				    }
				});
			}
			
			// create AJAX_SUGGESTION object
 			$(function() { 
 				new AJAX_SUGGESTION(
 						"search_field_id",               // input object id
 						invoke_ajax,                     // ajax call back function name
 						compose_li_tag,                  // list item composer function name
 						after_selection,                 // after_selection
 						300                              // result div width
 				); 
 			});
		</script>

 	<!-- BODY PART -->
	<input type=text id='search_field_id' size='20'>




[[ in SAMPLE JSON string for reference ]]
	{ data: 
		{nodes: [
			{seq:"1198", title:"python regular expression"},
			{seq:"1187", title:"python the OOP"},
			{seq:"1185", title:"python built-in data structure"},
			{seq:"1173", title:"multiple return values: python, ruby v.s. java"},
			{seq:"1168", title:"python file api"},
			{seq:"1156", title:"python the basics"},
		]}
	}



	
									That's all. Hope you to enjoy this....
									-- jazzvm
 */

	

/* constants */
var MIN_REQUIRED_LENGTH = 2;


var AJAX_SUGGESTION = function(input_element_id, ajax_request_fn, compose_li_tag_fn, call_back_fn_after_iterm_selection, result_div_width) {
	/* instance variables */
	this.input_object = $("#" + input_element_id);
	this.result_div_id = this.input_object.attr("id") + "_result";
	this.result_div_width = result_div_width; 
	
	this.key_input_activated = false;
	this.selected_item_index = -1;
	

	/* register ajax request fn for customizing purpose */
	this.ajax_request_fn = ajax_request_fn;

	/* register list tag composer fn for customizing purpose */
	this.compose_li_tag_fn = compose_li_tag_fn;
	
	/* register call back function invoked by entering or clicking list item */
	this.call_back_fn_after_iterm_selection = call_back_fn_after_iterm_selection;
	
	/* to start service */
	this.register_event();
}


AJAX_SUGGESTION.prototype = {

	init_variables: function() {
		this.selected_item_index = -1;
		$("#" + this.result_div_id).remove();
	},
		 
		
	register_event: function() {
		this.input_object.text("enter keyword");

		/* should be invoked only once */
		this.input_object.bind( "focus", {container:this}, function(e){
			if( !e.data.container.key_input_activated ) {
				e.data.container.input_object.val('');
			}
			e.data.container.key_input_activated = true;
		}); 

		this.input_object.bind( "keyup", {container:this}, function(e){
			e.data.container.respond_to_key_input(e);
		}); 
	}, 




	
	respond_to_key_input: function(e) {
		/*
		 * Javascript Char Codes (Key Codes) preference
		 * http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx 
		 */
	   	var keyId = (window.event) ? event.keyCode : e.keyCode;
		var text = this.input_object.val();

		//console.log('new input: ' + keyId + ' / text: ' + text);
		
		if( text.length > 0 ) {
			
			// 27|229: escape keys
			if( keyId == 27 ) {
				this.init_variables();
				return;
			}

			// 38|40: up arrow| down arrow
			if( keyId == 38 || keyId == 40 ) {
				this.navigate_list_item(keyId);
				return;
			}   

			// 13: enter will fire [call_back_fn_after_iterm_selection]
			if( keyId == 13 ) {
				if( this.selected_item_index != -1 ) {
					var li = this.find_selected_li_object();
					
					this.call_back_fn_after_iterm_selection(li);
					this.init_variables();
				}
				return;
			}

			// other cases will fire ajax request
			if(	text.length > MIN_REQUIRED_LENGTH ) {
				this.init_variables();
				this.ajax_request_fn(this);
			}
		}
	}, 


	
	

	navigate_list_item: function(key_id) {
		var result_div = $('#' + this.result_div_id);
		
		if( this.result_div_id ) {
			var li_array = $('#' + this.result_div_id + '> ul').children();
			li_array.each(function (i) { set_default_li_style(this); });
			
			if( key_id == 38 ) { // up
				if( this.selected_item_index > 0 ) this.selected_item_index--;
				if( this.selected_item_index > -1 ) {
					set_selected_li_style( $(li_array[this.selected_item_index]) );
				}
			} else {			 // down
				if( this.selected_item_index < li_array.length-1 ) this.selected_item_index++;
				if( this.selected_item_index < li_array.length ) {
					set_selected_li_style( $(li_array[this.selected_item_index]) );
				}
			}

			$(this.input_object).val( $(this.input_object).val() );
			
		} else {
			//console.log("result div doesn't even exist");
		}
	},
	


	render_result: function(response, this_object) {
		var data = response.data.nodes;

		var result_div_tag = $("<div id='" + this_object.result_div_id + "'></div>");
		set_div_style(result_div_tag, this_object);

		$(result_div_tag).appendTo( $("body") );

		
		var ul_tag  = $("<ul></ul>");
		set_ul_style(ul_tag);

		for(var i=0; i<data.length; i++) {

			var li_tag  = this_object.compose_li_tag_fn(data[i]);

			li_tag.bind('click', {this_object:this_object, li_tag:li_tag}, function(e){
				var my_obj = e.data.this_object;
				my_obj.call_back_fn_after_iterm_selection(e.data.li_tag);
				my_obj.init_variables();
			}); 

			set_default_li_style(li_tag);
			li_tag.appendTo(ul_tag);
		}		
		$(ul_tag).appendTo(result_div_tag);
	},



	/* 
	 * [utility functions] 
	 */
	find_selected_li_object: function() {
		var result_div = $('#' + this.result_div_id);

		if( this.result_div_id ) {
			var li_array = $('#' + this.result_div_id + '> ul').children();
			return li_array[this.selected_item_index];
		} else {
			return null;
		}
	}, 
	
	
	
	
}


/*
 * ######################################################################
 * ######################## GLOBAL FUNCTIONS ############################
 * ######################################################################
 */

/*
 * POSITION object
 */
function get_destination_position( the_object ) {
	var input_object = the_object.input_object;
	var position = $(input_object).offset();

	position.top = position.top + input_object.height();

	/* comment follow line */
	position.left = position.left - (the_object.result_div_width - input_object.width()) + 10; 

	return position;
}


/*
 * CSS Attributes
 * http://msdn.microsoft.com/en-us/library/aa358806(VS.85).aspx
 */
function set_div_style(tag, the_object) {
	var position = get_destination_position(the_object);
	
	$(tag).css("position", "absolute");
	$(tag).css("z-index", 1000);
	$(tag).css('padding-bottom', '0px');
	$(tag).css('padding-left', '0px');
	$(tag).css('padding-right', '0px');
	$(tag).css('padding-top', '6px');
	$(tag).css("top", position.top);
	$(tag).css("left", position.left);
	$(tag).css("width", the_object.result_div_width);
}
function set_ul_style(tag) {
	//$(tag).css('width', '100%');
	$(tag).css('border', '1px solid #ddd');
	$(tag).css('background', 'ivory');
	$(tag).css('margin', '0px');
	$(tag).css('padding', '0px');
	$(tag).css('list-style', 'none');
}
function set_default_li_style(tag) {
	$(tag).css('font-size', 'small');
	$(tag).css('background', '#ECE5B6');
	//$(tag).css('margin-left', '3px');
	//$(tag).css('margin-right', '3px');
	$(tag).css('padding-top', '2px');
	$(tag).css('overflow', 'hidden');
	$(tag).css('color', '#C11B17');
	$(tag).css('border-bottom', '1px dotted');
}
function set_selected_li_style(tag) {
	$(tag).css('background', '#488AC7');
	$(tag).css('color', '#FFFFFF');
}

