
/*===============================================================================
	TimePicker.js
	John Larson
	9/27/08
	
	Add time picking input+icon+widget to a span!
	
	Example:
	<span id="billingWidgetDateFrom"></span>
	
	myTimePicker = new TimePicker('dueDateTime',
		{initialValue:		'3:30pm',
		 timeFormat:		'%i:%N%p',
		 inputClass:		'TimePicker',
		 hideOnBlur:		true,
		 showOnInputFocus:	false });

		 showPickerIcon:	true,
	Credits:
		Adapted from nogray_time_picker.js 
		The TimePicker class (see below)
		License:  MIT-style license. 
		provided by the NoGray.com
		by Wesam Saif
		support: support@nogray.com
	
===============================================================================*/


/*
	
Options:
	visible:
		Indicates either the time picker would be visible or not.
		
	offset:
		The timepicker by default will show at the lower left corner of the input field. offset allows for offsetting the time picker position.
		
	startTimeFieldSetFieldSet:
		The initial time (as an object) for the time picker (hour between 0 to 23, minute between 0 to 59)
	
	selectedTime:
		The initial selected time (as an object) for the time picker (hour between 0 to 23, minute between 0 to 59)
		
	imagesPath:
		The path to the images folder that hold the time picker images (without a trailing slash).
		
	faceImage:
		The clock face image.
		
	hourHandImage:
		The hour hand image (one image for 12 hours using CSS spirits).
		
	minuteHandImage:
		The minute hand image (one image for 60 minutes using CSS spirits).
		
	centerImage:
		The center image for the clock (the hands nob).
	
	closeImage:
		The close button image (like an image of a red X, for example)
	
	clockSize:
		Because there could be a delay in loading the images, the clockSize is the image faceImage size.
	
	hourHandSize:
		The width and height of the hourHangImage to show one hour (the total width divided by 12). Give or take 1 pixel.
		
	minuteHandSize:
		The width and height of the minuteHangImage to show one minute (the total width divided by 60). Give or take 1 pixel.
		
	centerSize:
		The width and height of the centerImage.
	
	ampmStyles:
		The CSS style object for the AM/PM link.
		
	lang:
		The language variables for AM/PM. Also used for user input.
		
	hideOthersOnShow:
		Close all the open time pickers when the current time picker is open.
	
	showCloseIcon:
		Show the little red x that enables the user to close the clock.
				

Events:
	onOpen:
		Event that will be fired when the time picker is opened.
		
	onClose:
		Event that will be fired when the time picker is closed.
		
	onChange:
		Event that will be fired when the time is changed.
			

Variables:
	this.timeFieldSet:
		the current time (either initial or selected)
		
	
Function:
	updateAmPm:
		update the text link HTML value from am to pm or the opposite
			
	moveHands:
		move the clock hands based on the time selected
		
	clickAngle:
		return the angle from the user click
		
	updateField:
		update the input field
		
	openTimePicker:
		open the time picker
		
		
*/

var TimePicker = new Class({
	
	Implements: [Options, Events],
	
	options: {
		visible:				false,
		offset:					{x:0, y:0},
		initialValue:			'',
		timeFormat:				'%i:%N%p',
		startTimeFieldSet:		{hour:new Date().getHours(), 
								 minute: new Date().getMinutes()},
		selectedTime:			null,
		imagesPath:				"images/timePicker",
		faceImage:				"clockFace.gif",
		hourHandImage:			"clockHours.gif",
		minuteHandImage:		"clockMinutes.gif",
		centerImage:			"clockCenter.gif",
		closeImage:				"closeImage.gif",
		clockSize:				{width:142, height:142},
		hourHandSize:			{width: 67, height: 68},
		minuteHandSize:			{width:111, height:112},
		centerSize:				{width:  7, height:  6},
		ampmStyles:				{'fontSize':'10pt',
								 'fontWeight':'bold',
								 'color':'#999999',
								 'textDecoration':'none'},
		lang:					{'am':'AM', 'pm':'PM'},
		hideOthersOnShow:		true,
		showOnInputFocus:		true,
		hideOnBlur:				true,
		showCloseIcon:			true,
		showPickerIcon:			true,
		className:				'TimePicker',
		inputClass:				'TimePicker-input',
		pickerPositionOptions:	{offset: {x: 0, y: 0}},
		onOpen:					$empty,
		onClose:				$empty,
		onChange: 				$empty
	},
	
	initialize: function(container, inputName, options) {
	
		if(!$(container))
			throw('Unknown container passed to TimePicker contructor (' + container + ')');
		this.container = $(container);
		this.setOptions(options);
		
		if(this.container.getStyle('position') == 'static')
			this.container.setStyle('position', 'relative');
		
		// Create the input:
		var theInput = new Element('input');
		theInput.type = 'text';
		theInput.name = inputName;
		if(this.options.inputID)  theInput.ID = this.options.inputID;
		theInput.addClass(this.options.inputClass);
		theInput.maxLength	= 10;
		
		this.theInput = theInput;
		this.isVisible = false;
		
		
		// What's our initially chosen time?
		if(Date.parseTime(this.options.initialValue))
			this.timeFieldSet = Date.parseTime(this.options.initialValue)
		else
			this.timeFieldSet = this.options.startTimeFieldSet
		
		this.updateField();
		
		var me = this;
		
		this.container.adopt(theInput);
		
		
		// Now the icon, if called for:
		if(this.options.showPickerIcon) {
			var theIcon = new Element('span');
			theIcon.addClass(this.options.className + '-Icon'); // MUST be defined in CSS
			theIcon.addEvent('click', me.showPickerWidget.bind(me));
			theIcon.setStyle('vertical-align', 'middle');
			this.container.adopt(theIcon);
		}
		
		// Now to create the widget itself:
		this.moveEl = {};
		this.moveEl['move'] = false;
		
		if ($defined(this.options.selectedTime))
			this.timeFieldSet = this.options.selectedTime;
		else
			this.timeFieldSet = this.options.startTimeFieldSet;
		
		
	
		var widgetDiv = new Element('div', {
			'styles':{	display:	'none',
						position:	'absolute',
						width:		this.options.clockSize.width,
						height:		this.options.clockSize.height,
						'z-index':	20000},
			'class': this.options.className + '-WidgetDiv'
		});
		
		var clockFace = new Element('img', {
			'src': this.options.imagesPath + "/" + this.options.faceImage,
			'styles':{	position:	'absolute',
						width:		this.options.clockSize.width,
						'z-index':	20001}
		});
		widgetDiv.adopt(clockFace);
		
		// IE6 fix... neccessary?  To be verified!
		if ((window.ie) && (!window.ie7)){
			var iframe = new Element("iframe", {
				src:		'about:Blank',
				styles:		{width:			this.options.clockSize.width,
							 position:		'absolute',
							 'z-index':		20000,
							 height:		this.options.clockSize.height,
							 frameborder:	0}
			});
			widgetDiv.adopt(iframe);					
		}
		
		
		// Build the clock, one piece at a time!
		this.minuteHand = new Element("div", {
			styles: {	marginTop:	((this.options.clockSize.height - this.options.minuteHandSize.height)/2).toInt(),
						marginLeft:	((this.options.clockSize.width - this.options.minuteHandSize.width)/2).toInt(),
						width:		this.options.minuteHandSize.width,
						height:		this.options.minuteHandSize.height,
						position:	'absolute',
						'z-index':	20001,
						background:	'url(' + this.options.imagesPath + "/" + this.options.minuteHandImage+') no-repeat top left'
					}
		});
		widgetDiv.adopt(this.minuteHand);
		
		
		this.hourHand = new Element("div", {
			styles: {	marginTop:	((this.options.clockSize.height - this.options.hourHandSize.height)/2).toInt(),
						marginLeft:	((this.options.clockSize.width - this.options.hourHandSize.width)/2).toInt(),
						width:		this.options.hourHandSize.width,
						height:		this.options.hourHandSize.height,
						position:	'absolute',
						'z-index':	20002,
						background:	'url(' + this.options.imagesPath + "/" + this.options.hourHandImage+') no-repeat top left'
					}
		});
		widgetDiv.adopt(this.hourHand);
		
		
		var clockCenter = new Element("img", {
			src:	this.options.imagesPath + "/" + this.options.centerImage,
			styles:{	position:	'absolute',
						'z-index':	20004,
						marginTop:	((this.options.clockSize.height - this.options.centerSize.height)/2).toInt(),
						marginLeft:	((this.options.clockSize.width - this.options.centerSize.width)/2).toInt()
					}
				});
		widgetDiv.adopt(clockCenter);
		
		
		this.ampm = new Element ("a", {
			styles:	this.options.ampmStyles,
			href:	'#'
		});
		
		this.updateAmPm();
		
		this.ampm.setStyles({
			position:	'absolute',
			'z-index':	20005,
			display:	'block',
			marginTop:	((this.options.clockSize.height + (this.options.clockSize.height/4))/2).toInt(),
			marginLeft:	((this.options.clockSize.width-20)/2).toInt()});
		widgetDiv.adopt(this.ampm);
		
		
		this.ampm.addEvent("click", function(e){
		//	dbug.log('click the AmPm!');
			var e = new Event(e);
			e.stop();
			this.timeFieldSet.hour = ((this.timeFieldSet.hour + 12) %24);
			this.updateAmPm();
			this.updateField();	
			this.fireEvent("onChange");
		}.bind(this));
		
		
	//	this.container.adopt(widgetDiv);
		widgetDiv.injectInside(document.body);
		
		this.widgetDiv = widgetDiv;
		
		this.moveHands();
		
		this.widgetDiv.addEvent("mousedown", function(e){
			var e = new Event(e);
			var coord = this.widgetDiv.getCoordinates();
			
		//	log(coord);
			var ang = this.clickAngle({x:e.client.x, y:e.client.y}, coord);
			var h_ang = (this.timeFieldSet.hour%12) * 30;
			var m_ang = this.timeFieldSet.minute * 6;
			
			this.moveEl['move'] = true;
			this.moveEl['coord'] = coord;
			
			if (Math.abs(ang - m_ang) < Math.abs(ang - h_ang))
				this.moveEl['el'] = "minute";
			else if(Math.abs(ang - m_ang) > Math.abs(ang - h_ang))
				this.moveEl['el'] = "hour";
			else {
				if (e.target.getStyle("backgroundImage").indexOf(this.options.hourHandImage) != -1)
					this.moveEl['el'] = "hour";
				else
					this.moveEl['el'] = "minute";
			}
			
		}.bind(this));
		
		this.widgetDiv.addEvent("mouseup", function(){
			this.moveEl = {};
			this.moveEl['move'] = false;
		}.bind(this));		
		
		this.widgetDiv.addEvent("mousemove", function(e){
			if (this.moveEl['move']){
				var e = new Event(e);
				var add;
				
				var ang = this.clickAngle({x:e.client.x, y:e.client.y}, this.moveEl.coord);
				if (this.moveEl.el == "hour") var ang_by = 30;
				else var ang_by = 6;
			
				if (this.moveEl.el == "hour"){
					var h = (ang/ang_by).toInt();
					if (!isNaN(h))
						this.timeFieldSet.hour = h;
					
					if (this.ampm.innerHTML == this.options.lang.pm)
						this.timeFieldSet.hour = (this.timeFieldSet.hour+12)%24;
				}
				else{
					var m = (ang/ang_by).toInt();
					if (!isNaN(m))
						this.timeFieldSet.minute = m;
				}
											
				this.moveHands();
				this.updateField();
				
				this.fireEvent("onChange");
			}
			
		}.bind(this));
		
		
		
		if(this.options.showOnInputFocus  ||  !this.options.showPickerIcon)
			theInput.addEvent('focus', me.showPickerWidget.bind(me));
		
		if(this.options.hideOnBlur)
			this.widgetDiv.addEvent('outerClick', function() {
				if(me.isVisible)
					me.hidePickerWidget.bind(me)();
			});
		
		
		if (this.options.showCloseIcon) {
			var closeImage = new Element('img', {
				src:	this.options.imagesPath + "/" + this.options.closeImage,
				styles:	{position:		'absolute',
						 right:			5,
						 'z-index':		20006,
						 cursor:		'pointer'}
			});
			closeImage.addEvent("click", function(){
			//	dbug.log('click on closeImage!');
			//	dbug.log(me);
				me.hidePickerWidget();
			});
			
			this.widgetDiv.adopt(closeImage);
		}
		
		_all_page_timepickers.push(this);
		
	},
	
	
	showPickerWidget: function() {
		
		// What time should we show as chosen on our clock hands?
	//	if(Date.isValidDate(Date.parse(this.theInput.value))) {
	//		this.chosenDate = Date.parse(this.theInput.value);
	//		this.viewingDate = this.chosenDate;
	//	}
		
		if (this.options.hideOthersOnShow) $hideAllOtherTimePickers(this);
		
		// Extract these values from the this.theInput.value:
		if(Date.parseTime(this.theInput.value))
			this.timeFieldSet = Date.parseTime(this.theInput.value);
		else
			this.timeFieldSet = this.options.startTimeFieldSet;

		this.moveHands();
		this.resetWidgetPosition();
		this.widgetDiv.appear();
		setTimeout(function() {this.isVisible = true;}.bind(this),  500);

	},
	
	hidePickerWidget: function() {
	//	dbug.log('hide!');
		this.widgetDiv.fade();
		this.isVisible = false;
	},
	
	
	convertTimeToPreferredFormat: function(value) {
		if(Date.parse(value))
			return Date.format(Date.parse(value), this.options.timeFormat);
		else
			return null;
	},
	
	
	
	updateAmPm: function() {
		if (this.timeFieldSet.hour < 12) this.ampm.setHTML(this.options.lang.am);
		else this.ampm.setHTML(this.options.lang.pm);
	},
	
	moveHands: function(){
		try {
			this.hourHand.setStyle("backgroundPosition", (((this.timeFieldSet.hour % 12) *  this.options.hourHandSize.width) * -1));
			this.minuteHand.setStyle("backgroundPosition", ((this.timeFieldSet.minute * this.options.minuteHandSize.width) * -1));
		}
		catch(e){}
	},
	
	clickAngle: function(pnt, coord){
		var c_x = coord.width/2;
		var c_y = coord.height/2;
		var x = pnt.x + window.getScrollLeft() - coord.left;
		var y = pnt.y + window.getScrollTop() - coord.top;
			
		var t_x = c_x;
		var t_y = y;
		
		var CA = t_x - x;
		var CO = t_y - c_y;
		var AO = Math.sqrt(Math.pow(CA, 2) + Math.pow(CO, 2));
		
		var ang = Math.round((Math.acos((Math.pow(Math.abs(CA), 2) - Math.pow(Math.abs(AO), 2) - Math.pow(CO, 2))/(2 * CO * AO))) * 180/Math.PI);
		
		if (x < c_x) ang = 360 - ang;
		
		return ang;
	},
	
	
	
	updateField: function(){
		var theTime = this.timeFieldSet.hour + ':' + this.timeFieldSet.minute;
		this.theInput.value = Date.parse('1/1/08 ' + theTime).format(this.options.timeFormat);
	},
	
	openTimePicker: function(){
		if (this.options.hideOthersOnShow) $hideAllOtherTimePickers();
		if ($defined(this.theInput)){		
			var coord = this.theInput.getCoordinates();
			var top = coord.top+coord.height+this.options.offset.y;
			var left = coord.left+this.options.offset.x;
		}
		else {
			var coord = this.widgetDiv.getCoordinates();
			var top = coord.top;
			var left = coord.left;
		}
		
		this.widgetDiv.setStyles({
		//	   'top': top,
		//	   'left': left,
			   'opacity':1
			});
		
		this.fireEvent("onOpen");
	},
	
	
	resetWidgetPosition: function() {
		this.widgetDiv.setPosition($merge({
			relativeTo: this.container
		}, this.options.pickerPositionOptions));
	}
});

// array to hold all the time pickers on the page
var _all_page_timepickers = [];

// function to close all the open time pickers
var $hideAllOtherTimePickers = function(theException){
	_all_page_timepickers.each(function(timePickerWidget){
			if(timePickerWidget != theException)
				timePickerWidget.hidePickerWidget();
		});
};