/*
 * jQuery UI Timepicker
 *
 * Copyright 2010-2013, Francois Gelinas
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://fgelinas.com/code/timepicker
 *
 * Depends:
 *	jquery.ui.core.js
 *  jquery.ui.position.js (only if position settings are used)
 *
 * Change version 0.1.0 - moved the t-rex up here
 *
												  ____
	   ___                                      .-~. /_"-._
	  `-._~-.                                  / /_ "~o\  :Y
		  \  \                                / : \~x.  ` ')
		   ]  Y                              /  |  Y< ~-.__j
		  /   !                        _.--~T : l  l<  /.-~
		 /   /                 ____.--~ .   ` l /~\ \<|Y
		/   /             .-~~"        /| .    ',-~\ \L|
	   /   /             /     .^   \ Y~Y \.^>/l_   "--'
	  /   Y           .-"(  .  l__  j_j l_/ /~_.-~    .
	 Y    l          /    \  )    ~~~." / `/"~ / \.__/l_
	 |     \     _.-"      ~-{__     l  :  l._Z~-.___.--~
	 |      ~---~           /   ~~"---\_  ' __[>
	 l  .                _.^   ___     _>-y~
	  \  \     .      .-~   .-~   ~>--"  /
	   \  ~---"            /     ./  _.-'
		"-.,_____.,_  _.--~\     _.-~
					~~     (   _}       -Row
						   `. ~(
							 )  \
							/,`--'~\--'~\
				  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
							 ->T-Rex<-
*/
// phpcs:ignoreFile
(function ($) {

	$.extend( $.ui, { timepicker: { version: "0.3.3"} } );

	var PROP_NAME = 'timepicker',
		tpuuid    = new Date().getTime();

	/* Time picker manager.
	Use the singleton instance of this class, $.timepicker, to interact with the time picker.
	Settings for (groups of) time pickers are maintained in an instance object,
	allowing multiple different settings on the same page. */

	function Timepicker() {
		this.debug              = true; // Change this to true to start debugging
		this._curInst           = null; // The current instance in use
		this._disabledInputs    = []; // List of time picker inputs that have been disabled
		this._timepickerShowing = false; // True if the popup picker is showing , false if not
		this._inDialog          = false; // True if showing within a "dialog", false if not
		this._dialogClass       = 'ui-timepicker-dialog'; // The name of the dialog marker class
		this._mainDivId         = 'ui-timepicker-div'; // The ID of the main timepicker division
		this._inlineClass       = 'ui-timepicker-inline'; // The name of the inline marker class
		this._currentClass      = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
		this._dayOverClass      = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class

		this.regional     = []; // Available regional settings, indexed by language code
		this.regional[''] = { // Default regional settings
			hourText: 'Hour',           // Display text for hours section
			minuteText: 'Minute',       // Display text for minutes link
			amPmText: ['AM', 'PM'],     // Display text for AM PM
			closeButtonText: 'Done',        // Text for the confirmation button (ok button)
			nowButtonText: 'Now',           // Text for the now button
			deselectButtonText: 'Deselect'  // Text for the deselect button
		};
		this._defaults    = { // Global defaults for all the time picker instances
			showOn: 'focus',    // 'focus' for popup on focus,
								// 'button' for trigger button, or 'both' for either (not yet implemented)
			button: null,                   // 'button' element that will trigger the timepicker
			showAnim: 'fadeIn',             // Name of jQuery animation for popup
			showOptions: {},                // Options for enhanced animations
			appendText: '',                 // Display text following the input box, e.g. showing the format

			beforeShow: null,               // Define a callback function executed before the timepicker is shown
			onSelect: null,                 // Define a callback function when a hour / minutes is selected
			onClose: null,                  // Define a callback function when the timepicker is closed

			timeSeparator: ':',             // The character to use to separate hours and minutes.
			periodSeparator: ' ',           // The character to use to separate the time from the time period.
			showPeriod: false,              // Define whether or not to show AM/PM with selected time
			showPeriodLabels: true,         // Show the AM/PM labels on the left of the time picker
			showLeadingZero: true,          // Define whether or not to show a leading zero for hours < 10. [true/false]
			showMinutesLeadingZero: true,   // Define whether or not to show a leading zero for minutes < 10.
			altField: '',                   // Selector for an alternate field to store selected time into
			defaultTime: 'now',             // Used as default time when input field is empty or for inline timePicker
											// (set to 'now' for the current time, '' for no highlighted time)
			myPosition: 'left top',         // Position of the dialog relative to the input.
											// see the position utility for more info : http://jqueryui.com/demos/position/
			atPosition: 'left bottom',      // Position of the input element to match
											// Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
			// NEW: 2011-02-03
			onHourShow: null,			    // callback for enabling / disabling on selectable hours  ex : function(hour) { return true; }
			onMinuteShow: null,             // callback for enabling / disabling on time selection  ex : function(hour,minute) { return true; }

			hours: {
				starts: 0,                  // first displayed hour
				ends: 23                    // last displayed hour
			},
			minutes: {
				starts: 0,                  // first displayed minute
				ends: 55,                   // last displayed minute
				interval: 5,                // interval of displayed minutes
				manual: []                  // optional extra manual entries for minutes
			},
			rows: 4,                        // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
			// 2011-08-05 0.2.4
			showHours: true,                // display the hours section of the dialog
			showMinutes: true,              // display the minute section of the dialog
			optionalMinutes: false,         // optionally parse inputs of whole hours with minutes omitted

			// buttons
			showCloseButton: false,         // shows an OK button to confirm the edit
			showNowButton: false,           // Shows the 'now' button
			showDeselectButton: false,       // Shows the deselect time button

			maxTime: {
				hour: null,
				minute: null
			},
			minTime: {
				hour: null,
				minute: null
			}

		};
		$.extend( this._defaults, this.regional[''] );

		this.tpDiv = $( '<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>' );
	}

	$.extend(
		Timepicker.prototype,
		{
			/* Class name added to elements to indicate already configured with a time picker. */
			markerClassName: 'hasTimepicker',

			/* Debug logging (if enabled). */
			log: function () {
				if (this.debug) {
					console.log.apply( '', arguments );
				}
			},

			_widgetTimepicker: function () {
				return this.tpDiv;
			},

			/* Override the default settings for all instances of the time picker.
			@param  settings  object - the new settings to use as defaults (anonymous object)
			@return the manager object */
			setDefaults: function (settings) {
				extendRemove( this._defaults, settings || {} );
				return this;
			},

			/* Attach the time picker to a jQuery selection.
			@param  target    element - the target input field or division or span
			@param  settings  object - the new settings to use for this time picker instance (anonymous) */
			_attachTimepicker: function (target, settings) {
				// check for settings on the control itself - in namespace 'time:'
				var inlineSettings = null;
				for (var attrName in this._defaults) {
					var attrValue = target.getAttribute( 'time:' + attrName );
					if (attrValue) {
						inlineSettings = inlineSettings || {};
						try {
							inlineSettings[attrName] = eval( attrValue );
						} catch (err) {
							inlineSettings[attrName] = attrValue;
						}
					}
				}
				var nodeName = target.nodeName.toLowerCase();
				var inline   = (nodeName == 'div' || nodeName == 'span');

				if ( ! target.id) {
					this.uuid += 1;
					target.id  = 'tp' + this.uuid;
				}
				var inst      = this._newInst( $( target ), inline );
				inst.settings = $.extend( {}, settings || {}, inlineSettings || {} );
				if (nodeName == 'input') {
					this._connectTimepicker( target, inst );
					// init inst.hours and inst.minutes from the input value
					this._setTimeFromField( inst );
				} else if (inline) {
					this._inlineTimepicker( target, inst );
				}

			},

			/* Create a new instance object. */
			_newInst: function (target, inline) {
				var id = target[0].id.replace( /([^A-Za-z0-9_-])/g, '\\\\$1' ); // escape jQuery meta chars
				return {
					id: id, input: target, // associated target
					inline: inline, // is timepicker inline or not :
					tpDiv: ( ! inline ? this.tpDiv : // presentation div
					$( '<div class="' + this._inlineClass + ' ui-timepicker ui-widget  ui-helper-clearfix"></div>' ))
				};
			},

			/* Attach the time picker to an input field. */
			_connectTimepicker: function (target, inst) {
				var input    = $( target );
				inst.append  = $( [] );
				inst.trigger = $( [] );
				if (input.hasClass( this.markerClassName )) {
					return; }
				this._attachments( input, inst );
				input.addClass( this.markerClassName ).
				keydown( this._doKeyDown ).
				keyup( this._doKeyUp ).
				bind(
					"setData.timepicker",
					function (event, key, value) {
						inst.settings[key] = value;
					}
				).
				bind(
					"getData.timepicker",
					function (event, key) {
						return this._get( inst, key );
					}
				);
				$.data( target, PROP_NAME, inst );
			},

			/* Handle keystrokes. */
			_doKeyDown: function (event) {
				var inst       = $.timepicker._getInst( event.target );
				var handled    = true;
				inst._keyEvent = true;
				if ($.timepicker._timepickerShowing) {
					switch (event.keyCode) {
						case 9: $.timepicker._hideTimepicker();
							handled = false;
							break; // hide on tab out
						case 13:
							$.timepicker._updateSelectedValue( inst );
							$.timepicker._hideTimepicker();

							return false; // don't submit the form
						break; // select the value on enter
						case 27: $.timepicker._hideTimepicker();
							break; // hide on escape
						default: handled = false;
					}
				} else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
					$.timepicker._showTimepicker( this );
				} else {
					handled = false;
				}
				if (handled) {
					event.preventDefault();
					event.stopPropagation();
				}
			},

			/* Update selected time on keyUp */
			/* Added verion 0.0.5 */
			_doKeyUp: function (event) {
				var inst = $.timepicker._getInst( event.target );
				$.timepicker._setTimeFromField( inst );
				$.timepicker._updateTimepicker( inst );
			},

			/* Make attachments based on settings. */
			_attachments: function (input, inst) {
				var appendText = this._get( inst, 'appendText' );
				var isRTL      = this._get( inst, 'isRTL' );
				if (inst.append) {
					inst.append.remove(); }
				if (appendText) {
					inst.append = $( '<span class="' + this._appendClass + '">' + appendText + '</span>' );
					input[isRTL ? 'before' : 'after']( inst.append );
				}
				input.unbind( 'focus.timepicker', this._showTimepicker );
				input.unbind( 'click.timepicker', this._adjustZIndex );

				if (inst.trigger) {
					inst.trigger.remove(); }

				var showOn = this._get( inst, 'showOn' );
				if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
					input.bind( "focus.timepicker", this._showTimepicker );
					input.bind( "click.timepicker", this._adjustZIndex );
				}
				if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
					var button = this._get( inst, 'button' );

					// Add button if button element is not set
					if (button == null) {
						button = $( '<button class="ui-timepicker-trigger" type="button">...</button>' );
						input.after( button );
					}

					$( button ).bind(
						"click.timepicker",
						function () {
							if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
								$.timepicker._hideTimepicker();
							} else if ( ! inst.input.is( ':disabled' )) {
								$.timepicker._showTimepicker( input[0] );
							}
							return false;
						}
					);

				}
			},

			/* Attach an inline time picker to a div. */
			_inlineTimepicker: function(target, inst) {
				var divSpan = $( target );
				if (divSpan.hasClass( this.markerClassName )) {
					return;
				}
				divSpan.addClass( this.markerClassName ).append( inst.tpDiv ).
				bind(
					"setData.timepicker",
					function(event, key, value){
						inst.settings[key] = value;
					}
				).bind(
					"getData.timepicker",
					function(event, key){
						return this._get( inst, key );
					}
				);
				$.data( target, PROP_NAME, inst );

				this._setTimeFromField( inst );
				this._updateTimepicker( inst );
				inst.tpDiv.show();
			},

			_adjustZIndex: function(input) {
				input    = input.target || input;
				var inst = $.timepicker._getInst( input );
				inst.tpDiv.css( 'zIndex', $.timepicker._getZIndex( input ) + 1 );
			},

			/* Pop-up the time picker for a given input field.
			@param  input  element - the input field attached to the time picker or
			event - if triggered by focus */
			_showTimepicker: function (input) {
				input = input.target || input;
				if (input.nodeName.toLowerCase() != 'input') {
					input = $( 'input', input.parentNode )[0]; } // find from button/image trigger

				if ($.timepicker._isDisabledTimepicker( input ) || $.timepicker._lastInput == input) {
					return; } // already here

				// fix v 0.0.8 - close current timepicker before showing another one
				$.timepicker._hideTimepicker();

				var inst = $.timepicker._getInst( input );
				if ($.timepicker._curInst && $.timepicker._curInst != inst) {
					$.timepicker._curInst.tpDiv.stop( true, true );
				}
				var beforeShow = $.timepicker._get( inst, 'beforeShow' );
				extendRemove( inst.settings, (beforeShow ? beforeShow.apply( input, [input, inst] ) : {}) );
				inst.lastVal            = null;
				$.timepicker._lastInput = input;

				$.timepicker._setTimeFromField( inst );

				// calculate default position
				if ($.timepicker._inDialog) {
					input.value = ''; } // hide cursor
				if ( ! $.timepicker._pos) { // position below input
					$.timepicker._pos     = $.timepicker._findPos( input );
					$.timepicker._pos[1] += input.offsetHeight; // add the height
				}
				var isFixed = false;
				$( input ).parents().each(
					function () {
						isFixed | = $( this ).css( 'position' ) == 'fixed';
						return ! isFixed;
					}
				);

				var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };

				$.timepicker._pos = null;
				// determine sizing offscreen
				inst.tpDiv.css( { position: 'absolute', display: 'block', top: '-1000px' } );
				$.timepicker._updateTimepicker( inst );

				// position with the ui position utility, if loaded
				if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
					inst.tpDiv.position(
						{
							of: inst.input,
							my: $.timepicker._get( inst, 'myPosition' ),
							at: $.timepicker._get( inst, 'atPosition' ),
							// offset: $( "#offset" ).val(),
							// using: using,
							collision: 'flip'
						}
					);
					var offset        = inst.tpDiv.offset();
					$.timepicker._pos = [offset.top, offset.left];
				}

				// reset clicked state
				inst._hoursClicked   = false;
				inst._minutesClicked = false;

				// fix width for dynamic number of time pickers
				// and adjust position before showing
				offset = $.timepicker._checkOffset( inst, offset, isFixed );
				inst.tpDiv.css(
					{ position: ($.timepicker._inDialog && $.blockUI ?
						'static' : (isFixed ? 'fixed' : 'absolute')), display : 'none',
					left: offset.left + 'px', top: offset.top + 'px'
					}
				);
				if ( ! inst.inline ) {
					var showAnim = $.timepicker._get( inst, 'showAnim' );
					var duration = $.timepicker._get( inst, 'duration' );

					var postProcess = function () {
						$.timepicker._timepickerShowing = true;
						var borders                     = $.timepicker._getBorders( inst.tpDiv );
						inst.tpDiv.find( 'iframe.ui-timepicker-cover' ). // IE6- only
						css(
							{ left: -borders[0], top: -borders[1],
								width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
							}
						);
					};

					// Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
					$.timepicker._adjustZIndex( input );
					// inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);

					if ($.effects && $.effects[showAnim]) {
						inst.tpDiv.show( showAnim, $.timepicker._get( inst, 'showOptions' ), duration, postProcess );
					} else {
						inst.tpDiv.show( (showAnim ? duration : null), postProcess );
					}
					if ( ! showAnim || ! duration) {
						postProcess(); }
					if (inst.input.is( ':visible' ) && ! inst.input.is( ':disabled' )) {
						inst.input.focus(); }
					$.timepicker._curInst = inst;
				}
			},

			// This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility.
			// Enhancement returns maximum zindex value discovered while traversing parent elements,
			// rather than the first zindex value found. Ensures the timepicker popup will be in front,
			// even in funky scenarios like non-jq dialog containers with large fixed zindex values and
			// nested zindex-influenced elements of their own.
			_getZIndex: function (target) {
				var elem     = $( target );
				var maxValue = 0;
				var position, value;
				while (elem.length && elem[0] !== document) {
					position = elem.css( "position" );
					if (position === "absolute" || position === "relative" || position === "fixed") {
						value = parseInt( elem.css( "zIndex" ), 10 );
						if ( ! isNaN( value ) && value !== 0) {
							if (value > maxValue) {
								maxValue = value; }
						}
					}
					elem = elem.parent();
				}

				return maxValue;
			},

			/* Refresh the time picker
			@param   target  element - The target input field or inline container element. */
			_refreshTimepicker: function(target) {
				var inst = this._getInst( target );
				if (inst) {
					this._updateTimepicker( inst );
				}
			},

			/* Generate the time picker content. */
			_updateTimepicker: function (inst) {
				inst.tpDiv.empty().append( this._generateHTML( inst ) );
				this._rebindDialogEvents( inst );

			},

			_rebindDialogEvents: function (inst) {
				var borders = $.timepicker._getBorders( inst.tpDiv ),
				self        = this;
				inst.tpDiv
				.find( 'iframe.ui-timepicker-cover' ) // IE6- only
				.css(
					{ left: -borders[0], top: -borders[1],
						width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
					}
				)
				.end()
				// after the picker html is appended bind the click & double click events (faster in IE this way
				// then letting the browser interpret the inline events)
				// the binding for the minute cells also exists in _updateMinuteDisplay
				.find( '.ui-timepicker-minute-cell' )
				.unbind()
				.bind( "click", { fromDoubleClick:false }, $.proxy( $.timepicker.selectMinutes, this ) )
				.bind( "dblclick", { fromDoubleClick:true }, $.proxy( $.timepicker.selectMinutes, this ) )
				.end()
				.find( '.ui-timepicker-hour-cell' )
				.unbind()
				.bind( "click", { fromDoubleClick:false }, $.proxy( $.timepicker.selectHours, this ) )
				.bind( "dblclick", { fromDoubleClick:true }, $.proxy( $.timepicker.selectHours, this ) )
				.end()
				.find( '.ui-timepicker td a' )
				.unbind()
				.bind(
					'mouseout',
					function () {
						$( this ).removeClass( 'ui-state-hover' );
						if (this.className.indexOf( 'ui-timepicker-prev' ) != -1) {
							$( this ).removeClass( 'ui-timepicker-prev-hover' );
						}
						if (this.className.indexOf( 'ui-timepicker-next' ) != -1) {
							$( this ).removeClass( 'ui-timepicker-next-hover' );
						}
					}
				)
				.bind(
					'mouseover',
					function () {
						if ( ! self._isDisabledTimepicker( inst.inline ? inst.tpDiv.parent()[0] : inst.input[0] )) {
							$( this ).parents( '.ui-timepicker-calendar' ).find( 'a' ).removeClass( 'ui-state-hover' );
							$( this ).addClass( 'ui-state-hover' );
							if (this.className.indexOf( 'ui-timepicker-prev' ) != -1) {
								$( this ).addClass( 'ui-timepicker-prev-hover' );
							}
							if (this.className.indexOf( 'ui-timepicker-next' ) != -1) {
								$( this ).addClass( 'ui-timepicker-next-hover' );
							}
						}
					}
				)
				.end()
				.find( '.' + this._dayOverClass + ' a' )
				.trigger( 'mouseover' )
				.end()
				.find( '.ui-timepicker-now' ).bind(
					"click",
					function(e) {
						$.timepicker.selectNow( e );
					}
				).end()
				.find( '.ui-timepicker-deselect' ).bind(
					"click",
					function(e) {
						$.timepicker.deselectTime( e );
					}
				).end()
				.find( '.ui-timepicker-close' ).bind(
					"click",
					function(e) {
						$.timepicker._hideTimepicker();
					}
				).end();
			},

			/* Generate the HTML for the current state of the time picker. */
			_generateHTML: function (inst) {

				var h, m, row, col, html, hoursHtml, minutesHtml = '',
				showPeriod                                       = (this._get( inst, 'showPeriod' ) == true),
				showPeriodLabels                                 = (this._get( inst, 'showPeriodLabels' ) == true),
				showLeadingZero                                  = (this._get( inst, 'showLeadingZero' ) == true),
				showHours                                        = (this._get( inst, 'showHours' ) == true),
				showMinutes                                      = (this._get( inst, 'showMinutes' ) == true),
				amPmText           = this._get( inst, 'amPmText' ),
				rows               = this._get( inst, 'rows' ),
				amRows             = 0,
				pmRows             = 0,
				amItems            = 0,
				pmItems            = 0,
				amFirstRow         = 0,
				pmFirstRow         = 0,
				hours              = Array(),
				hours_options      = this._get( inst, 'hours' ),
				hoursPerRow        = null,
				hourCounter        = 0,
				hourLabel          = this._get( inst, 'hourText' ),
				showCloseButton    = this._get( inst, 'showCloseButton' ),
				closeButtonText    = this._get( inst, 'closeButtonText' ),
				showNowButton      = this._get( inst, 'showNowButton' ),
				nowButtonText      = this._get( inst, 'nowButtonText' ),
				showDeselectButton = this._get( inst, 'showDeselectButton' ),
				deselectButtonText = this._get( inst, 'deselectButtonText' ),
				showButtonPanel    = showCloseButton || showNowButton || showDeselectButton;

				// prepare all hours and minutes, makes it easier to distribute by rows
				for (h = hours_options.starts; h <= hours_options.ends; h++) {
					hours.push( h );
				}
				hoursPerRow = Math.ceil( hours.length / rows ); // always round up

				if (showPeriodLabels) {
					for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
						if (hours[hourCounter] < 12) {
							amItems++;
						} else {
							pmItems++;
						}
					}
					hourCounter = 0;

					amRows = Math.floor( amItems / hours.length * rows );
					pmRows = Math.floor( pmItems / hours.length * rows );

					// assign the extra row to the period that is more densely populated
					if (rows != amRows + pmRows) {
						// Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
						if (amItems && ( ! pmItems || ! amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
							amRows++;
						} else {
							pmRows++;
						}
					}
					amFirstRow = Math.min( amRows, 1 );
					pmFirstRow = amRows + 1;

					if (amRows == 0) {
						hoursPerRow = Math.ceil( pmItems / pmRows );
					} else if (pmRows == 0) {
						hoursPerRow = Math.ceil( amItems / amRows );
					} else {
						hoursPerRow = Math.ceil( Math.max( amItems / amRows, pmItems / pmRows ) );
					}
				}

				html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';

				if (showHours) {

					html += '<td class="ui-timepicker-hours">' +
						'<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
						hourLabel +
						'</div>' +
						'<table class="ui-timepicker">';

					for (row = 1; row <= rows; row++) {
						html += '<tr>';
						// AM
						if (row == amFirstRow && showPeriodLabels) {
							html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
						}
						// PM
						if (row == pmFirstRow && showPeriodLabels) {
							html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
						}
						for (col = 1; col <= hoursPerRow; col++) {
							if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
								html += this._generateHTMLHourCell( inst, undefined, showPeriod, showLeadingZero );
							} else {
								html += this._generateHTMLHourCell( inst, hours[hourCounter], showPeriod, showLeadingZero );
								hourCounter++;
							}
						}
						html += '</tr>';
					}
					html += '</table>' + // Close the hours cells table
						'</td>'; // Close the Hour td
				}

				if (showMinutes) {
					html += '<td class="ui-timepicker-minutes">';
					html += this._generateHTMLMinutes( inst );
					html += '</td>';
				}

				html += '</tr>';

				if (showButtonPanel) {
					var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
					if (showNowButton) {
						buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
								   + ' data-timepicker-instance-id="#' + inst.id.replace( /\\\\/g,"\\" ) + '" >'
								   + nowButtonText + '</button>';
					}
					if (showDeselectButton) {
						buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
								   + ' data-timepicker-instance-id="#' + inst.id.replace( /\\\\/g,"\\" ) + '" >'
								   + deselectButtonText + '</button>';
					}
					if (showCloseButton) {
						buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
								   + ' data-timepicker-instance-id="#' + inst.id.replace( /\\\\/g,"\\" ) + '" >'
								   + closeButtonText + '</button>';
					}

					html += buttonPanel + '</div></td></tr>';
				}
				html += '</table>';

				return html;
			},

			/* Special function that update the minutes selection in currently visible timepicker
			* called on hour selection when onMinuteShow is defined  */
			_updateMinuteDisplay: function (inst) {
				var newHtml = this._generateHTMLMinutes( inst );
				inst.tpDiv.find( 'td.ui-timepicker-minutes' ).html( newHtml );
				this._rebindDialogEvents( inst );
				// after the picker html is appended bind the click & double click events (faster in IE this way
				// then letting the browser interpret the inline events)
				// yes I know, duplicate code, sorry
				/*                .find('.ui-timepicker-minute-cell')
					.bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
					.bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
				*/

			},

			/*
			* Generate the minutes table
			* This is separated from the _generateHTML function because is can be called separately (when hours changes)
			*/
			_generateHTMLMinutes: function (inst) {

				var m, row, html       = '',
				rows                   = this._get( inst, 'rows' ),
				minutes                = Array(),
				minutes_options        = this._get( inst, 'minutes' ),
				minutesPerRow          = null,
				minuteCounter          = 0,
				showMinutesLeadingZero = (this._get( inst, 'showMinutesLeadingZero' ) == true),
				onMinuteShow           = this._get( inst, 'onMinuteShow' ),
				minuteLabel            = this._get( inst, 'minuteText' );

				if ( ! minutes_options.starts) {
					minutes_options.starts = 0;
				}
				if ( ! minutes_options.ends) {
					minutes_options.ends = 59;
				}
				if ( ! minutes_options.manual) {
					minutes_options.manual = [];
				}
				for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
					minutes.push( m );
				}
				for (i = 0; i < minutes_options.manual.length;i++) {
					var currMin = minutes_options.manual[i];

					// Validate & filter duplicates of manual minute input
					if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray( currMin, minutes ) >= 0) {
						continue;
					}
					minutes.push( currMin );
				}

				// Sort to get correct order after adding manual minutes
				// Use compare function to sort by number, instead of string (default)
				minutes.sort(
					function(a, b) {
						return a - b;
					}
				);

				minutesPerRow = Math.round( minutes.length / rows + 0.49 ); // always round up

				/*
				 * The minutes table
				 */
				// if currently selected minute is not enabled, we have a problem and need to select a new minute.
				if (onMinuteShow &&
				(onMinuteShow.apply( (inst.input ? inst.input[0] : null), [inst.hours , inst.minutes] ) == false) ) {
					// loop minutes and select first available
					for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
						m = minutes[minuteCounter];
						if (onMinuteShow.apply( (inst.input ? inst.input[0] : null), [inst.hours, m] )) {
							inst.minutes = m;
							break;
						}
					}
				}

				html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
					minuteLabel +
					'</div>' +
					'<table class="ui-timepicker">';

				minuteCounter = 0;
				for (row = 1; row <= rows; row++) {
					html += '<tr>';
					while (minuteCounter < row * minutesPerRow) {
						var m           = minutes[minuteCounter];
						var displayText = '';
						if (m !== undefined ) {
							displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
						}
						html += this._generateHTMLMinuteCell( inst, m, displayText );
						minuteCounter++;
					}
					html += '</tr>';
				}

				html += '</table>';

				return html;
			},

			/* Generate the content of a "Hour" cell */
			_generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {

				var displayHour = hour;
				if ((hour > 12) && showPeriod) {
					displayHour = hour - 12;
				}
				if ((displayHour == 0) && showPeriod) {
					displayHour = 12;
				}
				if ((displayHour < 10) && showLeadingZero) {
					displayHour = '0' + displayHour;
				}

				var html       = "";
				var enabled    = true;
				var onHourShow = this._get( inst, 'onHourShow' );		// custom callback
				var maxTime    = this._get( inst, 'maxTime' );
				var minTime    = this._get( inst, 'minTime' );

				if (hour == undefined) {
					html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
					return html;
				}

				if (onHourShow) {
					enabled = onHourShow.apply( (inst.input ? inst.input[0] : null), [hour] );
				}

				if (enabled) {
					if ( ! isNaN( parseInt( maxTime.hour ) ) && hour > maxTime.hour ) {
						enabled = false;
					}
					if ( ! isNaN( parseInt( minTime.hour ) ) && hour < minTime.hour ) {
						enabled = false;
					}
				}

				if (enabled) {
					html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace( /\\\\/g,"\\" ) + '" data-hour="' + hour.toString() + '">' +
					   '<a class="ui-state-default ' +
					   (hour == inst.hours ? 'ui-state-active' : '') +
					   '">' +
					   displayHour.toString() +
					   '</a></td>';
				} else {
					html =
					'<td>' +
						'<span class="ui-state-default ui-state-disabled ' +
						(hour == inst.hours ? ' ui-state-active ' : ' ') +
						'">' +
						displayHour.toString() +
						'</span>' +
					'</td>';
				}
				return html;
			},

			/* Generate the content of a "Hour" cell */
			_generateHTMLMinuteCell: function (inst, minute, displayText) {
				 var html         = "";
				 var enabled      = true;
				 var hour         = inst.hours;
				 var onMinuteShow = this._get( inst, 'onMinuteShow' );		// custom callback
				 var maxTime      = this._get( inst, 'maxTime' );
				 var minTime      = this._get( inst, 'minTime' );

				if (onMinuteShow) {
					// NEW: 2011-02-03  we should give the hour as a parameter as well!
					enabled = onMinuteShow.apply( (inst.input ? inst.input[0] : null), [inst.hours,minute] );		// trigger callback
				}

				if (minute == undefined) {
					html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
					return html;
				}

				if (enabled && hour !== null) {
					if ( ! isNaN( parseInt( maxTime.hour ) ) && ! isNaN( parseInt( maxTime.minute ) ) && hour >= maxTime.hour && minute > maxTime.minute ) {
						enabled = false;
					}
					if ( ! isNaN( parseInt( minTime.hour ) ) && ! isNaN( parseInt( minTime.minute ) ) && hour <= minTime.hour && minute < minTime.minute ) {
						enabled = false;
					}
				}

				if (enabled) {
					html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace( /\\\\/g,"\\" ) + '" data-minute="' + minute.toString() + '" >' +
					  '<a class="ui-state-default ' +
					  (minute == inst.minutes ? 'ui-state-active' : '') +
					  '" >' +
					  displayText +
					  '</a></td>';
				} else {

					html = '<td>' +
					'<span class="ui-state-default ui-state-disabled" >' +
					   displayText +
					'</span>' +
					'</td>';
				}
				 return html;
			},

			/* Detach a timepicker from its control.
			@param  target    element - the target input field or division or span */
			_destroyTimepicker: function(target) {
				var $target = $( target );
				var inst    = $.data( target, PROP_NAME );
				if ( ! $target.hasClass( this.markerClassName )) {
					return;
				}
				var nodeName = target.nodeName.toLowerCase();
				$.removeData( target, PROP_NAME );
				if (nodeName == 'input') {
					inst.append.remove();
					inst.trigger.remove();
					$target.removeClass( this.markerClassName )
					.unbind( 'focus.timepicker', this._showTimepicker )
					.unbind( 'click.timepicker', this._adjustZIndex );
				} else if (nodeName == 'div' || nodeName == 'span') {
					$target.removeClass( this.markerClassName ).empty();
				}
			},

			/* Enable the date picker to a jQuery selection.
			@param  target    element - the target input field or division or span */
			_enableTimepicker: function(target) {
				var $target = $( target ),
				target_id   = $target.attr( 'id' ),
				inst        = $.data( target, PROP_NAME );

				if ( ! $target.hasClass( this.markerClassName )) {
					return;
				}
				var nodeName = target.nodeName.toLowerCase();
				if (nodeName == 'input') {
					target.disabled = false;
					var button      = this._get( inst, 'button' );
					$( button ).removeClass( 'ui-state-disabled' ).disabled = false;
					inst.trigger.filter( 'button' ).
					each( function() { this.disabled = false; } ).end();
				} else if (nodeName == 'div' || nodeName == 'span') {
					var inline = $target.children( '.' + this._inlineClass );
					inline.children().removeClass( 'ui-state-disabled' );
					inline.find( 'button' ).each(
						function() { this.disabled = false }
					)
				}
				this._disabledInputs = $.map(
					this._disabledInputs,
					function(value) { return (value == target_id ? null : value); }
				); // delete entry
			},

			/* Disable the time picker to a jQuery selection.
			@param  target    element - the target input field or division or span */
			_disableTimepicker: function(target) {
				var $target = $( target );
				var inst    = $.data( target, PROP_NAME );
				if ( ! $target.hasClass( this.markerClassName )) {
					return;
				}
				var nodeName = target.nodeName.toLowerCase();
				if (nodeName == 'input') {
					var button = this._get( inst, 'button' );

					$( button ).addClass( 'ui-state-disabled' ).disabled = true;
					target.disabled                                      = true;

					inst.trigger.filter( 'button' ).
					each( function() { this.disabled = true; } ).end();

				} else if (nodeName == 'div' || nodeName == 'span') {
					var inline = $target.children( '.' + this._inlineClass );
					inline.children().addClass( 'ui-state-disabled' );
					inline.find( 'button' ).each(
						function() { this.disabled = true }
					)

				}
				this._disabledInputs                              = $.map(
					this._disabledInputs,
					function(value) { return (value == target ? null : value); }
				); // delete entry
				this._disabledInputs[this._disabledInputs.length] = $target.attr( 'id' );
			},

			/* Is the first field in a jQuery collection disabled as a timepicker?
			@param  target_id element - the target input field or division or span
			@return boolean - true if disabled, false if enabled */
			_isDisabledTimepicker: function (target_id) {
				if ( ! target_id) {
					return false; }
				for (var i = 0; i < this._disabledInputs.length; i++) {
					if (this._disabledInputs[i] == target_id) {
						return true; }
				}
				return false;
			},

			/* Check positioning to remain on screen. */
			_checkOffset: function (inst, offset, isFixed) {
				var tpWidth     = inst.tpDiv.outerWidth();
				var tpHeight    = inst.tpDiv.outerHeight();
				var inputWidth  = inst.input ? inst.input.outerWidth() : 0;
				var inputHeight = inst.input ? inst.input.outerHeight() : 0;
				var viewWidth   = document.documentElement.clientWidth + $( document ).scrollLeft();
				var viewHeight  = document.documentElement.clientHeight + $( document ).scrollTop();

				offset.left -= (this._get( inst, 'isRTL' ) ? (tpWidth - inputWidth) : 0);
				offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $( document ).scrollLeft() : 0;
				offset.top  -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $( document ).scrollTop() : 0;

				// now check if timepicker is showing outside window viewport - move to a better place if so.
				offset.left -= Math.min(
					offset.left,
					(offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
					Math.abs( offset.left + tpWidth - viewWidth ) : 0
				);
				offset.top  -= Math.min(
					offset.top,
					(offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
					Math.abs( tpHeight + inputHeight ) : 0
				);

				return offset;
			},

			/* Find an object's position on the screen. */
			_findPos: function (obj) {
				var inst  = this._getInst( obj );
				var isRTL = this._get( inst, 'isRTL' );
				while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
					obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
				}
				var position = $( obj ).offset();
				return [position.left, position.top];
			},

			/* Retrieve the size of left and top borders for an element.
			@param  elem  (jQuery object) the element of interest
			@return  (number[2]) the left and top borders */
			_getBorders: function (elem) {
				var convert = function (value) {
					return { thin: 1, medium: 2, thick: 3}[value] || value;
				};
				return [parseFloat( convert( elem.css( 'border-left-width' ) ) ),
				parseFloat( convert( elem.css( 'border-top-width' ) ) )];
			},

			/* Close time picker if clicked elsewhere. */
			_checkExternalClick: function (event) {
				if ( ! $.timepicker._curInst) {
					return; }
				var $target = $( event.target );
				if ($target[0].id != $.timepicker._mainDivId &&
				$target.parents( '#' + $.timepicker._mainDivId ).length == 0 &&
				! $target.hasClass( $.timepicker.markerClassName ) &&
				! $target.hasClass( $.timepicker._triggerClass ) &&
				$.timepicker._timepickerShowing && ! ($.timepicker._inDialog && $.blockUI)) {
					$.timepicker._hideTimepicker();
				}
			},

			/* Hide the time picker from view.
			@param  input  element - the input field attached to the time picker */
			_hideTimepicker: function (input) {
				var inst = this._curInst;
				if ( ! inst || (input && inst != $.data( input, PROP_NAME ))) {
					return; }
				if (this._timepickerShowing) {
					var showAnim    = this._get( inst, 'showAnim' );
					var duration    = this._get( inst, 'duration' );
					var postProcess = function () {
						$.timepicker._tidyDialog( inst );
						this._curInst = null;
					};
					if ($.effects && $.effects[showAnim]) {
						inst.tpDiv.hide( showAnim, $.timepicker._get( inst, 'showOptions' ), duration, postProcess );
					} else {
						inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
						(showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]( (showAnim ? duration : null), postProcess );
					}
					if ( ! showAnim) {
						postProcess(); }

					this._timepickerShowing = false;

					this._lastInput = null;
					if (this._inDialog) {
						this._dialogInput.css( { position: 'absolute', left: '0', top: '-100px' } );
						if ($.blockUI) {
							$.unblockUI();
							$( 'body' ).append( this.tpDiv );
						}
					}
					this._inDialog = false;

					var onClose = this._get( inst, 'onClose' );
					if (onClose) {
						onClose.apply(
							(inst.input ? inst.input[0] : null),
							[(inst.input ? inst.input.val() : ''), inst]
						);  // trigger custom callback
					}

				}
			},

			/* Tidy up after a dialog display. */
			_tidyDialog: function (inst) {
				inst.tpDiv.removeClass( this._dialogClass ).unbind( '.ui-timepicker' );
			},

			/* Retrieve the instance data for the target control.
			@param  target  element - the target input field or division or span
			@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 timepicker';
				}
			},

			/* Get a setting value, defaulting if necessary. */
			_get: function (inst, name) {
				return inst.settings[name] !== undefined ?
				inst.settings[name] : this._defaults[name];
			},

			/* Parse existing time and initialise time picker. */
			_setTimeFromField: function (inst) {
				if (inst.input.val() == inst.lastVal) {
					return; }
				var defaultTime = this._get( inst, 'defaultTime' );

				var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded( inst ) : defaultTime;
				if ((inst.inline == false) && (inst.input.val() != '')) {
					timeToParse = inst.input.val() }

				if (timeToParse instanceof Date) {
					inst.hours   = timeToParse.getHours();
					inst.minutes = timeToParse.getMinutes();
				} else {
					var timeVal = inst.lastVal = timeToParse;
					if (timeToParse == '') {
						inst.hours   = -1;
						inst.minutes = -1;
					} else {
						var time     = this.parseTime( inst, timeVal );
						inst.hours   = time.hours;
						inst.minutes = time.minutes;
					}
				}

				$.timepicker._updateTimepicker( inst );
			},

			/* Update or retrieve the settings for an existing time picker.
			@param  target  element - the target input field or division or span
			@param  name    object - the new settings to update or
						   string - the name of the setting to change or retrieve,
						   when retrieving also 'all' for all instance settings or
						   'defaults' for all global defaults
			@param  value   any - the new value for the setting
					   (omit if above is an object or to retrieve a value) */
			_optionTimepicker: function(target, name, value) {
				var inst = this._getInst( target );
				if (arguments.length == 2 && typeof name == 'string') {
					return (name == 'defaults' ? $.extend( {}, $.timepicker._defaults ) :
					(inst ? (name == 'all' ? $.extend( {}, inst.settings ) :
					this._get( inst, name )) : null));
				}
				var settings = name || {};
				if (typeof name == 'string') {
					settings       = {};
					settings[name] = value;
				}
				if (inst) {
					extendRemove( inst.settings, settings );
					if (this._curInst == inst) {
						this._hideTimepicker();
						this._updateTimepicker( inst );
					}
					if (inst.inline) {
						this._updateTimepicker( inst );
					}
				}
			},

			/* Set the time for a jQuery selection.
			@param  target  element - the target input field or division or span
			@param  time    String - the new time */
			_setTimeTimepicker: function(target, time) {
				var inst = this._getInst( target );
				if (inst) {
					this._setTime( inst, time );
					this._updateTimepicker( inst );
					this._updateAlternate( inst, time );
				}
			},

			/* Set the time directly. */
			_setTime: function(inst, time, noChange) {
				var origHours   = inst.hours;
				var origMinutes = inst.minutes;
				if (time instanceof Date) {
					inst.hours   = time.getHours();
					inst.minutes = time.getMinutes();
				} else {
					var time     = this.parseTime( inst, time );
					inst.hours   = time.hours;
					inst.minutes = time.minutes;
				}

				if ((origHours != inst.hours || origMinutes != inst.minutes) && ! noChange) {
					inst.input.trigger( 'change' );
				}
				this._updateTimepicker( inst );
				this._updateSelectedValue( inst );
			},

			/* Return the current time, ready to be parsed, rounded to the closest minute by interval */
			_getCurrentTimeRounded: function (inst) {
				var currentTime = new Date(),
				currentMinutes  = currentTime.getMinutes(),
				minutes_options = this._get( inst, 'minutes' ),
				// round to closest interval
				adjustedMinutes = Math.round( currentMinutes / minutes_options.interval ) * minutes_options.interval;
				currentTime.setMinutes( adjustedMinutes );
				return currentTime;
			},

			/*
			* Parse a time string into hours and minutes
			*/
			parseTime: function (inst, timeVal) {
				var retVal     = new Object();
				retVal.hours   = -1;
				retVal.minutes = -1;

				if ( ! timeVal) {
					return '';
				}

				var timeSeparator = this._get( inst, 'timeSeparator' ),
				amPmText          = this._get( inst, 'amPmText' ),
				showHours         = this._get( inst, 'showHours' ),
				showMinutes       = this._get( inst, 'showMinutes' ),
				optionalMinutes   = this._get( inst, 'optionalMinutes' ),
				showPeriod        = (this._get( inst, 'showPeriod' ) == true),
				p                 = timeVal.indexOf( timeSeparator );

				// check if time separator found
				if (p != -1) {
					retVal.hours   = parseInt( timeVal.substr( 0, p ), 10 );
					retVal.minutes = parseInt( timeVal.substr( p + 1 ), 10 );
				}
				// check for hours only
				else if ( (showHours) && ( ! showMinutes || optionalMinutes ) ) {
					retVal.hours = parseInt( timeVal, 10 );
				}
				// check for minutes only
				else if ( ( ! showHours) && (showMinutes) ) {
					retVal.minutes = parseInt( timeVal, 10 );
				}

				if (showHours) {
					var timeValUpper = timeVal.toUpperCase();
					if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf( amPmText[1].toUpperCase() ) != -1)) {
						retVal.hours += 12;
					}
					// fix for 12 AM
					if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf( amPmText[0].toUpperCase() ) != -1)) {
						retVal.hours = 0;
					}
				}

				return retVal;
			},

			selectNow: function(event) {
				var id  = $( event.target ).attr( "data-timepicker-instance-id" ),
				$target = $( id ),
				inst    = this._getInst( $target[0] );
				// if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
				var currentTime = new Date();
				inst.hours      = currentTime.getHours();
				inst.minutes    = currentTime.getMinutes();
				this._updateSelectedValue( inst );
				this._updateTimepicker( inst );
				this._hideTimepicker();
			},

			deselectTime: function(event) {
				var id       = $( event.target ).attr( "data-timepicker-instance-id" ),
				$target      = $( id ),
				inst         = this._getInst( $target[0] );
				inst.hours   = -1;
				inst.minutes = -1;
				this._updateSelectedValue( inst );
				this._hideTimepicker();
			},

			selectHours: function (event) {
				var $td         = $( event.currentTarget ),
				id              = $td.attr( "data-timepicker-instance-id" ),
				newHours        = parseInt( $td.attr( "data-hour" ) ),
				fromDoubleClick = event.data.fromDoubleClick,
				$target         = $( id ),
				inst            = this._getInst( $target[0] ),
				showMinutes     = (this._get( inst, 'showMinutes' ) == true);

				// don't select if disabled
				if ( $.timepicker._isDisabledTimepicker( $target.attr( 'id' ) ) ) {
					return false }

				$td.parents( '.ui-timepicker-hours:first' ).find( 'a' ).removeClass( 'ui-state-active' );
				$td.children( 'a' ).addClass( 'ui-state-active' );
				inst.hours = newHours;

				// added for onMinuteShow callback
				var onMinuteShow = this._get( inst, 'onMinuteShow' ),
				maxTime          = this._get( inst, 'maxTime' ),
				minTime          = this._get( inst, 'minTime' );
				if (onMinuteShow || ! isNaN( parseInt( maxTime.minute ) ) || ! isNaN( parseInt( minTime.minute ) )) {
					// this will trigger a callback on selected hour to make sure selected minute is allowed.
					this._updateMinuteDisplay( inst );
				}

				this._updateSelectedValue( inst );

				inst._hoursClicked = true;
				if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
					$.timepicker._hideTimepicker();
				}
				// return false because if used inline, prevent the url to change to a hashtag
				return false;
			},

			selectMinutes: function (event) {
				var $td         = $( event.currentTarget ),
				id              = $td.attr( "data-timepicker-instance-id" ),
				newMinutes      = parseInt( $td.attr( "data-minute" ) ),
				fromDoubleClick = event.data.fromDoubleClick,
				$target         = $( id ),
				inst            = this._getInst( $target[0] ),
				showHours       = (this._get( inst, 'showHours' ) == true);

				// don't select if disabled
				if ( $.timepicker._isDisabledTimepicker( $target.attr( 'id' ) ) ) {
					return false }

				$td.parents( '.ui-timepicker-minutes:first' ).find( 'a' ).removeClass( 'ui-state-active' );
				$td.children( 'a' ).addClass( 'ui-state-active' );

				inst.minutes = newMinutes;
				this._updateSelectedValue( inst );

				inst._minutesClicked = true;
				if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
					$.timepicker._hideTimepicker();
					// return false because if used inline, prevent the url to change to a hashtag
					return false;
				}

				// return false because if used inline, prevent the url to change to a hashtag
				return false;
			},

			_updateSelectedValue: function (inst) {
				var newTime = this._getParsedTime( inst );
				if (inst.input) {
					inst.input.val( newTime );
					inst.input.trigger( 'change' );
				}
				var onSelect = this._get( inst, 'onSelect' );
				if (onSelect) {
					onSelect.apply( (inst.input ? inst.input[0] : null), [newTime, inst] ); } // trigger custom callback
				this._updateAlternate( inst, newTime );
				return newTime;
			},

			/* this function process selected time and return it parsed according to instance options */
			_getParsedTime: function(inst) {

				if (inst.hours == -1 && inst.minutes == -1) {
					return '';
				}

				// default to 0 AM if hours is not valid
				if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) {
					inst.hours = 0; }
				// default to 0 minutes if minute is not valid
				if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) {
					inst.minutes = 0; }

				var period      = "",
				showPeriod      = (this._get( inst, 'showPeriod' ) == true),
				showLeadingZero = (this._get( inst, 'showLeadingZero' ) == true),
				showHours       = (this._get( inst, 'showHours' ) == true),
				showMinutes     = (this._get( inst, 'showMinutes' ) == true),
				optionalMinutes = (this._get( inst, 'optionalMinutes' ) == true),
				amPmText        = this._get( inst, 'amPmText' ),
				selectedHours   = inst.hours ? inst.hours : 0,
				selectedMinutes = inst.minutes ? inst.minutes : 0,
				displayHours    = selectedHours ? selectedHours : 0,
				parsedTime      = '';

				// fix some display problem when hours or minutes are not selected yet
				if (displayHours == -1) {
					displayHours = 0 }
				if (selectedMinutes == -1) {
					selectedMinutes = 0 }

				if (showPeriod) {
					if (inst.hours == 0) {
						displayHours = 12;
					}
					if (inst.hours < 12) {
						period = amPmText[0];
					} else {
						period = amPmText[1];
						if (displayHours > 12) {
							displayHours -= 12;
						}
					}
				}

				var h = displayHours.toString();
				if (showLeadingZero && (displayHours < 10)) {
					h = '0' + h; }

				var m = selectedMinutes.toString();
				if (selectedMinutes < 10) {
					m = '0' + m; }

				if (showHours) {
					parsedTime += h;
				}
				if (showHours && showMinutes && ( ! optionalMinutes || m != 0)) {
					parsedTime += this._get( inst, 'timeSeparator' );
				}
				if (showMinutes && ( ! optionalMinutes || m != 0)) {
					parsedTime += m;
				}
				if (showHours) {
					if (period.length > 0) {
						parsedTime += this._get( inst, 'periodSeparator' ) + period; }
				}

				return parsedTime;
			},

			/* Update any alternate field to synchronise with the main field. */
			_updateAlternate: function(inst, newTime) {
				var altField = this._get( inst, 'altField' );
				if (altField) { // update alternate field too
					$( altField ).each(
						function(i,e) {
							$( e ).val( newTime );
						}
					);
				}
			},

			_getTimeAsDateTimepicker: function(input) {
				var inst = this._getInst( input );
				if (inst.hours == -1 && inst.minutes == -1) {
					return '';
				}

				// default to 0 AM if hours is not valid
				if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) {
					inst.hours = 0; }
				// default to 0 minutes if minute is not valid
				if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) {
					inst.minutes = 0; }

				return new Date( 0, 0, 0, inst.hours, inst.minutes, 0 );
			},
			/* This might look unused but it's called by the $.fn.timepicker function with param getTime */
			/* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
			_getTimeTimepicker : function(input) {
				var inst = this._getInst( input );
				return this._getParsedTime( inst );
			},
			_getHourTimepicker: function(input) {
				var inst = this._getInst( input );
				if ( inst == undefined) {
					return -1; }
				return inst.hours;
			},
			_getMinuteTimepicker: function(input) {
				var inst = this._getInst( input );
				if ( inst == undefined) {
					return -1; }
				return inst.minutes;
			}

		}
	);

	/* Invoke the timepicker functionality.
	@param  options  string - a command, optionally followed by additional parameters or
	Object - settings for attaching new timepicker functionality
	@return  jQuery object */
	$.fn.timepicker = function (options) {
		/* Initialise the time picker. */
		if ( ! $.timepicker.initialized) {
			$( document ).mousedown( $.timepicker._checkExternalClick );
			$.timepicker.initialized = true;
		}

		 /* Append timepicker main container to body if not exist. */
		if ($( "#" + $.timepicker._mainDivId ).length === 0) {
			$( 'body' ).append( $.timepicker.tpDiv );
		}

		var otherArgs = Array.prototype.slice.call( arguments, 1 );
		if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute' )) {
			return $.timepicker['_' + options + 'Timepicker'].
				apply( $.timepicker, [this[0]].concat( otherArgs ) );
		}
		if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string') {
			return $.timepicker['_' + options + 'Timepicker'].
				apply( $.timepicker, [this[0]].concat( otherArgs ) );
		}
		return this.each(
			function () {
				typeof options == 'string' ?
				$.timepicker['_' + options + 'Timepicker'].
				apply( $.timepicker, [this].concat( otherArgs ) ) :
				$.timepicker._attachTimepicker( this, options );
			}
		);
	};

	/* jQuery extend now ignores nulls! */
	function extendRemove(target, props) {
		$.extend( target, props );
		for (var name in props) {
			if (props[name] == null || props[name] == undefined) {
				target[name] = props[name];
			}
		}
		return target;
	};

	$.timepicker             = new Timepicker(); // singleton instance
	$.timepicker.initialized = false;
	$.timepicker.uuid        = new Date().getTime();
	$.timepicker.version     = "0.3.3";

	// Workaround for #4055
	// Add another global to avoid noConflict issues with inline event handlers
	window['TP_jQuery_' + tpuuid] = $;

})( jQuery );
