/*!
 * jQuery optionPicker plugin v0.1
 *
 * Fernando Prieto
 *  
 
 INSTALACIÓN:
 
 -es necesaria la siguiene estructura en el html:
 
 <div class="tpParentholder" id="parentHolderId">  => este div debe tener un id para poder referenciarlo de manera inequívoca
 <input id="myInput" type="text" />
 <div class="tpOptionsHolder">
 	<ul>
 		<li>option 1</li>
 		<li>option 2</li>
 		...
 		<li>option N</li>
 	</ul>
 </div>
 </div>
 
 -inicialización del optionPicker:
 
$('#input1').optionpicker({
		classOptionsHolder: "hour_select",  => redefinimos la clase del contenedor de opciones a 'hour_select'
		classParentHolder: "line", => redefinimos la clase del contenedor del input a 'line'
		onOpen: openCallback,
		onChange: changeCallback,
		onClose: closeCallback,
});

 
 
 
 */
(function ($, undefined) {
	
var PROP_NAME = 'optionpicker';
var instActive = null;

	/**
	 * option picker manager.
	 * Use the singleton instance of this class, $.optionpicker, to interact with the optionpicker
	 * Settings for (groups of) option pickers are maintained in an instance object,
	 * allowing multiple different settings on the same page
	 */
	function Optionpicker() {
		//this._state = [];
		this._curInst = null; // The current instance in use
		this._isOpen = false; 
		this._defaults = { // Global defaults for all the option picker instances
			classOptionsHolder: "opOptionsHolder",
			classParentHolder: "opParentHolder",
			onChange: null, //Define a callback function when the optionpicker is changed
			onOpen: null, //Define a callback function when the optionpicker is open
			onClose: null //Define a callback function when the optionpicker is closed
		};
	}
	
	$.extend(Optionpicker.prototype, {

		/**
		 * Attach the option picker to a jQuery selection.
		 * 
		 * @param {HTMLElement} target
		 * @param {Object} settings
		 */
		_attachOptionpicker: function (target, settings) 
		{
			if (this._getInst(target)) {
				return FALSE;
			}
			
			var $target = $(target);
			var self = this;
			var inst = self._newInst($target);
			
			$.extend(inst.settings, self._defaults, settings);			
			
			parentHolder = $target.parents('.'+inst.settings.classParentHolder);
			inst.parentHolderId = parentHolder[0].id;
			inst.opOptions = parentHolder.find('.'+inst.settings.classOptionsHolder);			
			
			$.data(target, PROP_NAME, inst);
			
			//events for click on option (click on a hour) 			
			$('#'+inst.parentHolderId+' .'+inst.settings.classOptionsHolder+' li').mouseenter(function()
  		{
  			$(this).addClass('ui-state-hover');
  		}).mouseleave(function()
  		{
  			$(this).removeClass('ui-state-hover');
  		}).click(function()
		  {
		  	//debug $('body').append('calling change:'+target.id+'<br>');
		  	self._changeOptionpicker( target, $(this).text() );
		  });  

			//event: focus on target field (display optionpicker)
			$target.focus(function()
			{				
				//debug $('#output').append('focus');
				//$(this).siblings('.'+inst.settings.classOptionsHolder).show();
				$.optionpicker._openOptionpicker(target);							
				//inst.tpOptions.show();
			});
				
		},

		
		/* Close option picker if clicked elsewhere. */
		_checkExternalClick: function(event) {
			
			if (!$.optionpicker._curInst) {
				return;
			}
			var $target = $(event.target);
			
			if ( $.optionpicker._curInst._isOpen &&
					 $target[0].id != $.optionpicker._curInst.id &&
					 $target[0].id != $.optionpicker._curInst.opOptions[0].id &&
				   $target.parents('.'+$.optionpicker._curInst.settings.classOptionsHolder).length == 0 )
			{
				$.optionpicker._hideOptionpicker();
			}
			
		},		
		
		/**
		 * Change selected attribute of the optionpicker.
		 * 
		 * @param {HTMLElement} target
		 * @param {String} value
		 * @param {String} text
		 */
		_changeOptionpicker: function (target, value) 
		{
		
			var inst = this._getInst(target);
			var onChange = this._get(inst, 'onChange');
			
			//change target input
			$(target).val(value);
			
			if (onChange) {
				onChange.apply((inst.input ? inst.input[0] : null), [value, inst]);
			} 
			
			$.optionpicker._hideOptionpicker();
			
		},

		/* Hide the option picker from view.
		   @param  input  element - the input field attached to the option picker */
		_hideOptionpicker: function(input) 
		{
		
			var inst = this._curInst;
			if (!inst || (input && inst != $.data(input, PROP_NAME)))
				return;
				
			inst.opOptions.hide();
			inst._isOpen = false;
			
			var onClose = this._get(inst, 'onClose');
			if (onClose) {
				onClose.apply((inst.input ? inst.input[0] : null), [inst]);
			} 
			
		},		
		
		/**
		 * open optionpicker
		 * 
		 * @param {HTMLElement} target
		 */
		_openOptionpicker: function (target) 
		{
		
			var inst = this._getInst(target);
			if (!inst || inst.isOpen || inst.isDisabled) {
				return;
			}
			
			$.optionpicker._curInst = inst;
			
			var selectedValue = $(target).val();
			inst.opOptions.find('li').removeClass('ui-state-active');
			inst.opOptions.find("li:contains('"+selectedValue+"')").addClass('ui-state-active');
			
			inst.opOptions.show();
			inst._isOpen = true;
			
			var onOpen = this._get(inst, 'onOpen');
			if (onOpen) {
				onOpen.apply((inst.input ? inst.input[0] : null), [inst]);
			} 			

		},
		
		/**
		 * Create a new instance object
		 * 
		 * @param {HTMLElement} target
		 * @return {Object}
		 */
		_newInst: function(target) 
		{
			var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
			return {
				id: id, 
				input: target, 
				uid: Math.floor(Math.random() * 99999999),
				//isOpen: FALSE,
				//isDisabled: FALSE,
				settings: {}
			}; 
		},
		
		/**
		 * Retrieve the instance data for the target control.
		 * 
		 * @param {HTMLElement} target
		 * @return {Object} - the associated instance data
		 * @throws error if a jQuery problem getting data
		 */
		_getInst: function(target) 
		{
			try {
				return $.data(target, PROP_NAME);
			}
			catch (err) {
				throw 'Missing instance data for this optionpicker';
			}
		},
		
		/**
		 * Get a setting value, defaulting if necessary
		 * 
		 * @param {Object} inst
		 * @param {String} name
		 * @return {Mixed}
		 */
		_get: function(inst, name) {
			return inst.settings[name] !== undefined ? inst.settings[name] : this._defaults[name];
		}
	});

	/**
	 * Invoke the optionpicker functionality.
	 * 
	 * @param {Object|String} options
	 * @return {Object}
	 */
	$.fn.optionpicker = function (options) {

		$(document).mousedown($.optionpicker._checkExternalClick);
	
		var otherArgs = Array.prototype.slice.call(arguments, 1);
		if (typeof options == 'string' && options == 'isDisabled') {
			return $.optionpicker['_' + options + 'Optionpicker'].apply($.optionpicker, [this[0]].concat(otherArgs));
		}
		
		if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') {
			return $.optionpicker['_' + options + 'Optionpicker'].apply($.optionpicker, [this[0]].concat(otherArgs));
		}
		
		return this.each(function() {
			typeof options == 'string' ?
				$.optionpicker['_' + options + 'Optionpicker'].apply($.optionpicker, [this].concat(otherArgs)) :
				$.optionpicker._attachOptionpicker(this, options);
		});
	};
	
	$.optionpicker = new Optionpicker(); // singleton instance
	$.optionpicker.version = "0.1";
	
})(jQuery);




