(function($) {
	$.extend($.datepicker._defaults, { 'stepMinutes': 1, 'stepHours': 1, 'time24h': false, 'showTime': false, 'altTimeField': '' });
	$.datepicker._newInstOverride = $.datepicker._newInst;
	$.datepicker._newInst = function(target, inline) {
		var datepicker = $.datepicker._newInstOverride(target, inline);
		$.timepicker.init();
		return datepicker;
	}
	$.datepicker._connectDatepickerOverride = $.datepicker._connectDatepicker;
	$.datepicker._connectDatepicker = function(target, inst) {
		$.datepicker._connectDatepickerOverride(target, inst);
		if (this._get(inst, 'showTime')) { inst.settings['showButtonPanel'] = true; }
		var showOn = this._get(inst, 'showOn');
		if (showOn == 'button' || showOn == 'both') {
			inst.trigger.unbind('click');
			inst.trigger.click(function() {
				if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target)
					$.datepicker._hideDatepicker(null);
				else
					$.datepicker._showDatepicker(target);
				return false;
			});
		}
	};
	$.datepicker._showDatepickerOverride = $.datepicker._showDatepicker;
	$.datepicker._showDatepicker = function(input) {
		$.datepicker._showDatepickerOverride(input);
		input = input.target || input;
		if (input.nodeName.toLowerCase() != 'input') input = $('input', input.parentNode)[0];
		if ($.datepicker._isDisabledDatepicker(input)) return;
		var inst = $.datepicker._getInst(input), showTime = $.datepicker._get(inst, 'showTime');
		if (showTime) $.timepicker.show(input);
	};
	$.datepicker._checkExternalClickOverride = $.datepicker._checkExternalClick;
	$.datepicker._checkExternalClick = function(event) {
		if (!$.datepicker._curInst) return;
		var $target = $(event.target);
		if (($target.parents('#' + $.timepicker._mainDivId).length == 0)) {
			$.datepicker._checkExternalClickOverride(event);
		}
	};
	$.datepicker._selectDateOverload = $.datepicker._selectDate;
	$.datepicker._selectDate = function(id, dateStr) {
		var target = $(id), inst = this._getInst(target[0]), showTime = this._get(inst, 'showTime');
		if (showTime) {
			inst.inline = true;
			$.datepicker._selectDateOverload(id, dateStr);
			inst.inline = false;
			this._updateDatepicker(inst);
		} else {
			$.datepicker._selectDateOverload(id, dateStr);
		}
	}
	$.datepicker._hideDatepickerOverride = $.datepicker._hideDatepicker;
	$.datepicker._hideDatepicker = function(input, duration) {
		var inst = this._curInst;
		if (!inst || (input && inst != $.data(input, PROP_NAME))) return;
		var showTime = this._get(inst, 'showTime');
		if (input === undefined && showTime) {
			if (inst.input && inst._hasSelectedDate) {
				inst.input.val(this._formatDate(inst));
				inst.input.trigger('change'); // fire the change event
			}
			this._updateAlternate(inst);
			if (showTime) $.timepicker.update(this._formatDate(inst));
		}
		$.datepicker._hideDatepickerOverride(input, duration);
		if (showTime) $.timepicker.hide();
	};
	$.datepicker._updateDatepickerOverride = $.datepicker._updateDatepicker;
	$.datepicker._updateDatepicker = function(inst) { $.datepicker._updateDatepickerOverride(inst); $.timepicker.resize(); };
	function Timepicker(){}
	Timepicker.prototype = {
		init: function() {
			this._mainDivId = 'ui-timepicker-div';
			this._inputId = null;
			this._orgValue = null;
			this._orgHour = null;
			this._orgMinute = null;
			this._colonPos = -1;
			this._visible = false;
			if ($('#' + this._mainDivId).length == 0) {
				this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible" style="width: 88px; display: none; position: absolute;"></div>');
				this._generateHtml();
			}
		},
		show: function(input) {
			var inst = $.datepicker._getInst(input);
			this._time24h = $.datepicker._get(inst, 'time24h');
			this._altTimeField = $.datepicker._get(inst, 'altTimeField');
			var stepMinutes = parseInt($.datepicker._get(inst, 'stepMinutes'), 10) || 1;
			var stepHours = parseInt($.datepicker._get(inst, 'stepHours'), 10) || 1;
			if (60 % stepMinutes != 0) { stepMinutes = 1; }
			if (24 % stepHours != 0) { stepHours = 1; }
			$('#hourSlider').slider('option', 'max', 24 - stepHours);
			$('#hourSlider').slider('option', 'step', stepHours);
			$('#minuteSlider').slider('option', 'max', 60 - stepMinutes);
			$('#minuteSlider').slider('option', 'step', stepMinutes);
			this._inputId = input.id;
			if (!this._visible) {
				this._parseTime();
				this._orgValue = $('#' + this._inputId).val();
			}
			this.resize();
			$('#' + this._mainDivId).show();
			this._visible = true;
			var dpDiv = $('#' + $.datepicker._mainDivId),
				dpDivPos = dpDiv.position(),
				viewWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) + $(document).scrollLeft(),
				tpRight = this.tpDiv.offset().left + this.tpDiv.outerWidth();
			if (tpRight > viewWidth) {
				dpDiv.css('left', dpDivPos.left - (tpRight - viewWidth) - 5);
				this.tpDiv.css('left', dpDiv.offset().left + dpDiv.outerWidth() + 'px');
			}
		},
		update: function(fd) {
			var curTime = $('#' + this._mainDivId + ' span.fragHours').text() + ':' + $('#' + this._mainDivId + ' span.fragMinutes').text();
			if (!this._time24h) { curTime += ' ' + $('#' + this._mainDivId + ' span.fragAmpm').text(); }
			var curDate = $('#' + this._inputId).val();
			if (curDate) $('#' + this._inputId).val(fd + ' ' + curTime);
			if (this._altTimeField) { $(this._altTimeField).each(function() { $(this).val(curTime); }); }
		},
		hide: function() {
			this._visible = false;
			$('#' + this._mainDivId).hide();
		},
		resize: function() {
			var dpDiv = $('#' + $.datepicker._mainDivId),
				dpDivPos = dpDiv.position(),
				hdrHeight = $('#' + $.datepicker._mainDivId + ' > div.ui-datepicker-header:first-child').height();
			$('#' + this._mainDivId + ' > div.ui-datepicker-header:first-child').css('height', hdrHeight);
			this.tpDiv.css({ 'height': dpDiv.height(), 'top': dpDivPos.top, 'left': dpDivPos.left + dpDiv.outerWidth() + 'px' });
			$('#hourSlider').css('height', this.tpDiv.height() - (3.5 * hdrHeight));
			$('#minuteSlider').css('height', this.tpDiv.height() - (3.5 * hdrHeight));
		},
		_generateHtml: function() {
			var html = '';
			html += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all">';
			html += '<div class="ui-datepicker-title" style="margin:0">';
			html += '<span class="fragHours">08</span><span class="delim">:</span><span class="fragMinutes">45</span> <span class="fragAmpm"></span></div></div><table>';
			html += '<tr><th>Hour</th><th>Minute</th></tr>';
			html += '<tr><td align="center"><div id="hourSlider" class="slider"></div></td><td align="center"><div id="minuteSlider" class="slider"></div></td></tr>';
			html += '</table>';
			this.tpDiv.empty().append(html);
			$('body').append(this.tpDiv);
			var self = this;
			$('#hourSlider').slider({ orientation: "vertical", range: 'min', min: 0, max: 23, step: 1,
				slide: function(event, ui) { self._writeTime('hour', ui.value); },
				stop: function(event, ui) { $('#' + self._inputId).focus(); }
			});
			$('#minuteSlider').slider({ orientation: "vertical", range: 'min', min: 0, max: 59, step: 1,
				slide: function(event, ui) { self._writeTime('minute', ui.value); },
				stop: function(event, ui) { $('#' + self._inputId).focus(); }
			});
			$('#hourSlider > a').css('padding', 0);
			$('#minuteSlider > a').css('padding', 0);
		},
		_writeTime: function(type, value) {
			if (type == 'hour') {
				if (!this._time24h) {
					if (value < 12) { $('#' + this._mainDivId + ' span.fragAmpm').text('am'); }
					else { $('#' + this._mainDivId + ' span.fragAmpm').text('pm'); value -= 12; }
					if (value == 0) value = 12;
				} else { $('#' + this._mainDivId + ' span.fragAmpm').text(''); }
				if (value < 10) value = '0' + value;
				$('#' + this._mainDivId + ' span.fragHours').text(value);
			}
			if (type == 'minute') {
				if (value < 10) value = '0' + value;
				$('#' + this._mainDivId + ' span.fragMinutes').text(value);
			}
		},
		_parseTime: function() {
			var dt = $('#' + this._inputId).val();
			this._colonPos = dt.search(':');
			var m = 0, h = 0, a = '';
			if (this._colonPos != -1) {
				h = parseInt(dt.substr(this._colonPos - 2, 2), 10);
				m = parseInt(dt.substr(this._colonPos + 1, 2), 10);
				a = jQuery.trim(dt.substr(this._colonPos + 3, 3));
			}
			a = a.toLowerCase();
			if (a != 'am' && a != 'pm') { a = ''; }
			if (h < 0) h = 0;
			if (m < 0) m = 0;
			if (h > 23) h = 23;
			if (m > 59) m = 59;
			if (a == 'pm' && h < 12) h += 12;
			if (a == 'am' && h == 12) h = 0;
			if (m == 0) m = 59;
			if (h == 0) h = 23;
			this._setTime('hour', h);
			this._setTime('minute', m);
			this._orgHour = h;
			this._orgMinute = m;
		},
		_setTime: function(type, value) {
			if (isNaN(value)) value = 0;
			if (value < 0) value = 0;
			if (value > 23 && type == 'hour') value = 23;
			if (value > 59 && type == 'minute') value = 59;
			if (type == 'hour') { $('#hourSlider').slider('value', value); }
			if (type == 'minute') { $('#minuteSlider').slider('value', value); }
			this._writeTime(type, value);
		}
	};
	$.timepicker = new Timepicker();
})(jQuery);
