// base.js
// a base set of utility methods
// to extend the window.TAP object with page-specific utilities, use $.extend

// this allows us to safely use console.log, console.warn, etc. when the user isn't using firebug
if (!window.console || !console.firebug)
{
	var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
	"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];

	window.console = {};
	for (var i = 0; i < names.length; ++i)
		window.console[names[i]] = function(){};
}

// set up the window.TAP namespace
window.TAP = {

	// extend js objects to have Perl-like capabilities
	Hash: function(obj)
	{
		this.hash = obj;
		this.keys = function(){
			var keys = [];
			for (var i in this.hash)
				if (this.hash.hasOwnProperty(i))
					keys.push(i);
			return keys;
		};
		this.values = function(){
			var keys = this.keys();
			var values = [];
			for (var i in keys)
				values.push(this.hash[i]);
			return values;
		};
		this.get = function(key){
			return this.hash[key];
		};
		this.length = function(){
			return this.keys().length;
		};

		return this;
	},

	typeOf: function(value){
		var s = typeof value;
		if (s === 'object')
		{
			if (value)
			{
				if (typeof value.length === 'number' && !(value.propertyIsEnumerable('length')) && typeof value.splice === 'function')
				{
					s = 'array';
				}
			}
			else
			{
				s = 'null';
			}
		}
		return s;
	},

	// given the name of a select element, populate it by appending options
	fillSelect: function(name, data){
		var $select = $('select').filter(function(){
			return this.name == name;
		});

		if ( !$select.size() )
		{
			// console log maybe?
			return;
		}

		// what kind of data do we have?
		// for hashes, the key will be the option value and the value will be the option text
		// for arrays, the option value and text will be identical
		if (window.TAP.typeOf(data) == 'array')
		{
			var hash = {};
			for (var i in data)
				hash[data[i]] = data[i];
			data = hash;
		}

		// append options (note that we are not emptying the select first)
		var list = [];
		for (var i in data)
		{
			var $option = $('<option></option>');
			$option.val(i);
			$option.text(data[i]);
			list.push($option.get(0));
		}

		$select.append(list);
	},
	
	// for any specified form, construct an object that can manipulate it
	Form: function(name)
	{
		// set up a jQuery object for quick access to elements of this form
		var $form = $('form#' + name);
		if ($form.size() == 0) {
			console.log('form', name, 'not found!');
			// need to throw an exception instead of returning anything
			return false;
		}
		
		// create a structure to contain all form inputs that have valid names
		// this structure defines how to get and set values
		// in effect, we are creating a cache that allows for fast manipulation without having to use id attributes
		var hash = {};
		$form.find(':input').each(function(){
			// check for valid name
			if (!this.name) 
				return;
			
			switch (this.type) {
				case 'checkbox':
					var checkbox = this;
					hash[this.name] = {
						get: function(){
							return checkbox.checked;
						},
						// to set a checkbox to checked, use JSON::true in perl
						set: function(value){
							checkbox.checked = (value === true);
						}
					}
					break;
					
				case 'hidden':
					var text = this;
					hash[this.name] = {
						get: function(){
							return text.value;
						},
						set: function(value){
							text.value = value;
						}
					}
					break;
					
				case 'textarea':
					var textarea = this;
					hash[this.name] = {
						get: function(){
							return $(textarea).val();
						},
						set: function(value){
							$(textarea).val(value);
						}
					}
					break;
					
				case 'select-multiple':
				// select multiple really needs to be handled differently
				// we will cross that bridge when we come to it
				
				case 'select-one':
					// there may be an alternate text input associated with this select
					var select = this;
					var $text = $form.find('input[type=text]').filter(function(){
						return this.name == select.name;
					});
					
					if ($text.size() == 1)
					{
						hash[this.name] = {
							get: function(){
								return $text.val() ? $text.val() : $(select).val();
							},
							set: function(value){
								var $temp = $(select).find('option').filter(function(){
									return this.value === value;
								});
								
								if ($temp.size()) 
									$temp.get(0).selected = true;
								else 
									$text.val(value);
							}
						}
					}
					else
					{
						hash[this.name] = {
							get: function(){
								return $(select).val();
							},
							set: function(value){
								var $temp = $(select).find('option').filter(function(){
									return this.value == value;
								});
								
								if ($temp.size()) 
									$temp.get(0).selected = true;
							}
						}
					}
					break;
					
				case 'text':
					// text inputs may be tied to selects
					if (hash[this.name] == undefined) {
						//some textboxes may be tied together
						//Ex: phone number
						//could easily see if multiple textboxes share same name
						//if so combine together although
						//splitting would be the trick
						var text = this;
						hash[this.name] = {
							get: function(){
								return text.value;
							},
							set: function(value){
								text.value = value;
							}
						};
					}
					break;
					
				/*
				case 'password':
					// text inputs may be tied to selects
					if (hash[this.name] == undefined) {
						//some textboxes may be tied together
						//Ex: phone number
						//could easily see if multiple textboxes share same name
						//if so combine together although
						//splitting would be the trick
						var text = this;
						hash[this.name] = {
							get: function(){
								return text.value;
							},
							set: function(value){
								text.value = value;
							}
						};
					}
					break;
				*/
					
				case 'radio':
					// need to capture entire group for radio buttons
					if (hash[this.name] == undefined) {
						var list = [];
						var name = this.name;
						$form.find('input[type=radio]').filter(function(){
							return this.name == name;
						}).each(function(){
							list.push(this);
						});
						
						hash[this.name] = {
							get: function(){
								for (var i in list) {
									if (list[i].checked) 
										return list[i].value;
								}
								return;
							},
							set: function(value){
								for (var i in list) {
									if (list[i].value == value) 
										list[i].checked = true;
								}
							}
						}
					}
					break;
					
				default:
					break;
			}
		});
		
		// now that the hash is set up, we can return a simple object
		// which exposes two methods: fill and serialize
		var obj = {
			fill: function(record){
				for (var field in record) {
					if (hash[field] == undefined) 
						continue;
					else 
						hash[field].set(record[field]);
				}
			},
			
			serialize: function(){
				var data = {};
				for (var field in hash)
				{
					data[field] = hash[field].get();
					//console.log(hash[field].get());
				} 
					
				return data;
			}
		};
		
		return obj;
	},
	
	// slurp was created to grab the values from all inputs inside of a container despite the container type
	slurp: function(name){
		// set up a jQuery object for quick access to elements of this container
		var $container = $('#' + name);
		if ($container.size() == 0) {
			console.log('container', name, 'not found!');
			// need to throw an exception instead of returning anything
			return false;
		}
		
		// create a structure to contain all inputs that have valid names
		// this structure defines how to get and set values
		// in effect, we are creating a cache that allows for fast manipulation without having to use id attributes
		var hash = {};
		$container.find(':input').each(function(){
			// check for valid name
			if (!this.name) 
				return;
			
			switch (this.type) {
				case 'checkbox':
					var checkbox = this;
					hash[this.name] = {
						get: function(){
							return checkbox.checked;
						},
						// to set a checkbox to checked, use JSON::true in perl
						set: function(value){
							checkbox.checked = (value === true);
						}
					}
					break;
					
				case 'hidden':
					var text = this;
					hash[this.name] = {
						get: function(){
							return text.value;
						},
						set: function(value){
							text.value = value;
						}
					}
					break;
					
				case 'textarea':
					var textarea = this;
					hash[this.name] = {
						get: function(){
							return $(textarea).val();
						},
						set: function(value){
							$(textarea).val(value);
						}
					}
					break;
					
				case 'select-multiple':
				// select multiple really needs to be handled differently
				// we will cross that bridge when we come to it
				
				case 'select-one':
					// there may be an alternate text input associated with this select
					var select = this;
					var $text = $container.find('input[type=text]').filter(function(){
						return this.name == select.name;
					});
					
					if ($text.size() == 1)
					{
						hash[this.name] = {
							get: function(){
								return $text.val() ? $text.val() : $(select).val();
							},
							set: function(value){
								var $temp = $(select).find('option').filter(function(){
									return this.value === value;
								});
								
								if ($temp.size()) 
									$temp.get(0).selected = true;
								else 
									$text.val(value);
							}
						}
					}
					else
					{
						hash[this.name] = {
							get: function(){
								return $(select).val();
							},
							set: function(value){
								var $temp = $(select).find('option').filter(function(){
									return this.value == value;
								});
								
								if ($temp.size()) 
									$temp.get(0).selected = true;
							}
						}
					}
					break;
					
				case 'text':
					// text inputs may be tied to selects
					if (hash[this.name] == undefined) {
						//some textboxes may be tied together
						//Ex: phone number
						//could easily see if multiple textboxes share same name
						//if so combine together although
						//splitting would be the trick
						var text = this;
						hash[this.name] = {
							get: function(){
								return text.value;
							},
							set: function(value){
								text.value = value;
							}
						};
					}
					break;
					
				/*
				case 'password':
					// text inputs may be tied to selects
					if (hash[this.name] == undefined) {
						//some textboxes may be tied together
						//Ex: phone number
						//could easily see if multiple textboxes share same name
						//if so combine together although
						//splitting would be the trick
						var text = this;
						hash[this.name] = {
							get: function(){
								return text.value;
							},
							set: function(value){
								text.value = value;
							}
						};
					}
					break;
				*/
					
				case 'radio':
					// need to capture entire group for radio buttons
					if (hash[this.name] == undefined) {
						var list = [];
						var name = this.name;
						$container.find('input[type=radio]').filter(function(){
							return this.name == name;
						}).each(function(){
							list.push(this);
						});
						
						hash[this.name] = {
							get: function(){
								for (var i in list) {
									if (list[i].checked) 
										return list[i].value;
								}
								return;
							},
							set: function(value){
								for (var i in list) {
									if (list[i].value == value) 
										list[i].checked = true;
								}
							}
						}
					}
					break;
					
				default:
					break;
			}
		});
		
		// now that the hash is set up, we can return a simple object
		// which exposes two methods: fill and serialize
		var obj = {
			fill: function(record){
				for (var field in record) {
					if (hash[field] == undefined) 
						continue;
					else 
						hash[field].set(record[field]);
				}
			},
			
			serialize: function(){
				var data = {};
				for (var field in hash)
				{
					data[field] = hash[field].get();
				} 
					
				return data;
			}
		};
		
		return obj;
	},

	/* DEPRECATED - see window.TAP.Form for a more elegant solution */	
	// fill out a form with a js object
	fillForm: function(record)
	{
		console.log('fillForm invoked');
		for (var field in record )
		{
			//find input
			//we may augment the record to include namespace for forms
			//var $obj = $(':input[name=' + field + ']');
			var field_selector = '#' + field.replace(/\./g, '\\.');
			var $obj = $(field_selector);
			if ( $obj.size() )
			{
				//determine what it is and what to do
				switch ( $obj.get(0).type )
				{
					case 'hidden' :
					case 'text' :
					case 'textarea' :
						$obj.val( record[field] );
						break;

					case 'radio' :
						$obj = $('input').filter(function(){ return $(this).attr('name') == field; });
						var $temp = $obj.filter('input[value=' + record[field] + ']')
						if ( $temp.size() )
                                                        $temp.get(0).checked = true;
						else if ( window.console )
                                              ;      //console.warn(field, 'found, but no matching value found');
						break;

					case 'checkbox' :
						if ( record[field] == 't' )
							$obj.get(0).checked = true;
						break;

					case 'select-multiple' :
					case 'select-one' :
						var $temp = $obj.find('option[value=' + record[field] + ']');
						if ( $temp.size() )
						{
							$temp.get(0).selected = true;
						}
						else
						{
							$temp = $('input').filter(function(){
								return $(this).attr('name') == field && this.type == 'text';
							});
							if ($temp.size())
								$temp.val( record[field] );
							else
								console.warn(field, 'found, but no matching option'); 
						}
						break;

					default:
						if (window.console)
							; //console.warn(field, 'found, but did not match any known input type');
				}
			}
			else if ( window.console )
				; //console.warn('WARNING: Input not found for ' + field);
		}

	},

	/* DEPRECATED - see window.TAP.Form for a more elegant solution */	
	// this method takes a selector and returns a hash of inputs that is suitable
	// for use as data in a jQuery ajax request
	serialize: function(selector){
		var data = {};

		$(selector).each(function(){
			var name = $(this).attr('name');
			if (name)
			{
				if ( this.type == 'radio' )
				{
					if ( $(this).attr('checked') == true )
						data[name] = $(this).val();
				}
				else if ( this.type == 'checkbox' )
				{
					if (this.checked)
						data[name] = 't';
					else
						data[name] = 'f';
				}
				else
					data[name] = $(this).val();
			}
		});
	
		return data;
	}
};
