/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20 * http://trentrichardson.com/examples/timepicker * Modified version of jQuery Timepicker * Copyright (c) 2016 Trent Richardson; Licensed MIT */ ( function( factory ) { 'use strict'; if ( typeof window.define === 'function' && window.define.amd ) { window.define( [ 'jquery', 'jquery-ui' ], factory ); } else { factory( window.jQuery ); } }( function( $ ) { 'use strict'; /* * jQuery extend now ignores nulls! */ var extendRemove = function( target, props ) { var name; $.extend( target, props ); for ( name in props ) { if ( props[ name ] === null || props[ name ] === undefined ) { target[ name ] = props[ name ]; } } return target; }; /* * jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype, * it will return false for all objects */ var isEmptyObject = function( obj ) { var prop; for ( prop in obj ) { if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { return false; } } return true; }; /* * Determine by the time format which units are supported * Returns an object of booleans for each unit */ var detectSupport = function( timeFormat ) { var tf = timeFormat.replace( /'.*?'/g, '' ).toLowerCase(), // removes literals isIn = function( f, t ) { // does the format contain the token? return f.indexOf( t ) !== -1 ? true : false; }; return { hour: isIn( tf, 'h' ), minute: isIn( tf, 'm' ), second: isIn( tf, 's' ), millisec: isIn( tf, 'l' ), microsec: isIn( tf, 'c' ), timezone: isIn( tf, 'z' ), ampm: isIn( tf, 't' ) && isIn( timeFormat, 'h' ), iso8601: isIn( timeFormat, 'Z' ) }; }; /* * Converts 24 hour format into 12 hour * Returns 12 hour without leading 0 */ var convert24to12 = function( hour ) { hour %= 12; if ( hour === 0 ) { hour = 12; } return String( hour ); }; var computeEffectiveSetting = function( settings, property ) { return settings && settings[ property ] ? settings[ property ] : $.tm_timepicker._defaults[ property ]; }; /* * Splits datetime string into date and time substrings. * Throws exception when date can't be parsed * Returns {dateString: dateString, timeString: timeString} */ var splitDateTime = function( dateTimeString, timeSettings ) { // The idea is to get the number separator occurrences in datetime and the time format requested (since time has // fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split. var separator = computeEffectiveSetting( timeSettings, 'separator' ), format = computeEffectiveSetting( timeSettings, 'timeFormat' ), timeParts = format.split( separator ), // how many occurrences of separator may be in our format? timePartsLen = timeParts.length, allParts = dateTimeString.split( separator ), allPartsLen = allParts.length; if ( allPartsLen > 1 ) { return { dateString: allParts.splice( 0, allPartsLen - timePartsLen ).join( separator ), timeString: allParts.splice( 0, timePartsLen ).join( separator ) }; } return { dateString: dateTimeString, timeString: '' }; }; /* * Internal function to parse datetime interval * Returns: {date: Date, timeObj: Object}, where * date - parsed date without time (type Date) * timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional */ var parseDateTimeInternal = function( dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings ) { var date, parts, parsedTime; parts = splitDateTime( dateTimeString, timeSettings ); date = $.tm_datepicker._base_parseDate( dateFormat, parts.dateString, dateSettings ); if ( parts.timeString === '' ) { return { date: date }; } parsedTime = $.tm_datepicker.parseTime( timeFormat, parts.timeString, timeSettings ); if ( ! parsedTime ) { throw 'Wrong time format'; } return { date: date, timeObj: parsedTime }; }; /* * Internal function to set timezone_select to the local timezone */ var selectLocalTimezone = function( tp_inst, date ) { var now; if ( tp_inst && tp_inst.timezone_select ) { now = date || new Date(); tp_inst.timezone_select.val( -now.getTimezoneOffset() ); } }; /* * Timepicker manager. * Use the singleton instance of this class, $.tm_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. */ var Timepicker = function() { this.regional = []; // Available regional settings, indexed by language code this.regional[ '' ] = { // Default regional settings currentText: 'Now', closeText: 'Done', amNames: [ 'AM', 'A' ], pmNames: [ 'PM', 'P' ], timeFormat: 'HH:mm', timeSuffix: '', timeOnlyTitle: 'Choose Time', timeText: 'Time', hourText: 'Hour', minuteText: 'Minute', secondText: 'Second', millisecText: 'Millisecond', microsecText: 'Microsecond', timezoneText: 'Time Zone', isRTL: false }; this._defaults = { // Global defaults for all the datetime picker instances showButtonPanel: true, timeOnly: false, timeOnlyShowDate: false, showHour: null, showMinute: null, showSecond: null, showMillisec: null, showMicrosec: null, showTimezone: null, showTime: true, stepHour: 1, stepMinute: 1, stepSecond: 1, stepMillisec: 1, stepMicrosec: 1, hour: 0, minute: 0, second: 0, millisec: 0, microsec: 0, timezone: null, hourMin: 0, minuteMin: 0, secondMin: 0, millisecMin: 0, microsecMin: 0, hourMax: 23, minuteMax: 59, secondMax: 59, millisecMax: 999, microsecMax: 999, minDateTime: null, maxDateTime: null, maxTime: null, minTime: null, onSelect: null, hourGrid: 0, minuteGrid: 0, secondGrid: 0, millisecGrid: 0, microsecGrid: 0, alwaysSetTime: true, separator: ' ', altFieldTimeOnly: true, altTimeFormat: null, altSeparator: null, altTimeSuffix: null, altRedirectFocus: true, pickerTimeFormat: null, pickerTimeSuffix: null, showTimepicker: true, timezoneList: null, addSliderAccess: false, sliderAccessArgs: null, controlType: 'slider', oneLine: false, defaultValue: null, parse: 'strict', afterInject: null }; $.extend( this._defaults, this.regional[ '' ] ); }; /* * Lets not redefine tm_timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded" */ $.ui.tm_timepicker = $.ui.tm_timepicker || {}; if ( $.ui.tm_timepicker.version ) { return; } /* * Extend jQueryUI, get it started with our version number */ $.extend( $.ui, { tm_timepicker: { version: '1.6.3' } } ); $.extend( Timepicker.prototype, { $input: null, $altInput: null, $timeObj: null, inst: null, hour_slider: null, minute_slider: null, second_slider: null, millisec_slider: null, microsec_slider: null, timezone_select: null, maxTime: null, minTime: null, hour: 0, minute: 0, second: 0, millisec: 0, microsec: 0, timezone: null, hourMinOriginal: null, minuteMinOriginal: null, secondMinOriginal: null, millisecMinOriginal: null, microsecMinOriginal: null, hourMaxOriginal: null, minuteMaxOriginal: null, secondMaxOriginal: null, millisecMaxOriginal: null, microsecMaxOriginal: null, ampm: '', formattedDate: '', formattedTime: '', formattedDateTime: '', timezoneList: null, units: [ 'hour', 'minute', 'second', 'millisec', 'microsec' ], support: {}, control: null, /* * Override the default settings for all instances of the time picker. * @param {Object} settings object - the new settings to use as defaults (anonymous object) * @return {Object} the manager object */ setDefaults: function( settings ) { extendRemove( this._defaults, settings || {} ); return this; }, /* * Create a new Timepicker instance */ _newInst: function( $input, opts ) { var tp_inst = new Timepicker(), inlineSettings = {}, fns = {}, overrides, i; var attrName; var attrValue; var timezoneList; var tzl; var tzi = 0; var tzv = null; for ( attrName in this._defaults ) { if ( Object.prototype.hasOwnProperty.call( this._defaults, attrName ) ) { attrValue = $input.attr( 'data-' + attrName ); if ( attrValue ) { inlineSettings[ attrName ] = attrValue; } } } overrides = { beforeShow: function( input, dp_inst ) { if ( typeof tp_inst._defaults.evnts.beforeShow === 'function' ) { return tp_inst._defaults.evnts.beforeShow.call( $input[ 0 ], input, dp_inst, tp_inst ); } }, onChangeMonthYear: function( year, month, dp_inst ) { // Update the time as well : this prevents the time from disappearing from the $input field. // tp_inst._updateDateTime(dp_inst); if ( typeof tp_inst._defaults.evnts.onChangeMonthYear === 'function' ) { tp_inst._defaults.evnts.onChangeMonthYear.call( $input[ 0 ], year, month, dp_inst, tp_inst ); } }, onClose: function( dateText, dp_inst ) { if ( tp_inst.timeDefined === true && $input.val() !== '' ) { tp_inst._updateDateTime( dp_inst ); } if ( typeof tp_inst._defaults.evnts.onClose === 'function' ) { tp_inst._defaults.evnts.onClose.call( $input[ 0 ], dateText, dp_inst, tp_inst ); } } }; for ( i in overrides ) { if ( Object.prototype.hasOwnProperty.call( overrides, i ) ) { fns[ i ] = opts[ i ] || this._defaults[ i ] || null; } } tp_inst._defaults = $.extend( {}, this._defaults, inlineSettings, opts, overrides, { evnts: fns, tm_timepicker: tp_inst // add tm_timepicker as a property of tm_datepicker: $.tm_datepicker._get(dp_inst, 'tm_timepicker'); } ); tp_inst.amNames = $.map( tp_inst._defaults.amNames, function( val ) { return val.toUpperCase(); } ); tp_inst.pmNames = $.map( tp_inst._defaults.pmNames, function( val ) { return val.toUpperCase(); } ); // detect which units are supported tp_inst.support = detectSupport( tp_inst._defaults.timeFormat + ( tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '' ) + ( tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : '' ) ); // controlType is string - key to our this._controls if ( typeof tp_inst._defaults.controlType === 'string' ) { if ( tp_inst._defaults.controlType === 'slider' && typeof $.ui.slider === 'undefined' ) { tp_inst._defaults.controlType = 'select'; } tp_inst.control = tp_inst._controls[ tp_inst._defaults.controlType ]; } else { // controlType is an object and must implement create, options, value methods tp_inst.control = tp_inst._defaults.controlType; } // prep the timezone options timezoneList = [ -720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60, 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840 ]; if ( tp_inst._defaults.timezoneList !== null ) { timezoneList = tp_inst._defaults.timezoneList; } tzl = timezoneList.length; if ( tzl > 0 && typeof timezoneList[ 0 ] !== 'object' ) { for ( ; tzi < tzl; tzi += 1 ) { tzv = timezoneList[ tzi ]; timezoneList[ tzi ] = { value: tzv, label: $.tm_timepicker.timezoneOffsetString( tzv, tp_inst.support.iso8601 ) }; } } tp_inst._defaults.timezoneList = timezoneList; // set the default units tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.tm_timepicker.timezoneOffsetNumber( tp_inst._defaults.timezone ) : new Date().getTimezoneOffset() * -1; tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin : tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour; tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin : tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute; tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin : tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second; tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin : tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec; tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin : tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec; tp_inst.ampm = ''; tp_inst.$input = $input; if ( tp_inst._defaults.altField ) { tp_inst.$altInput = $( tp_inst._defaults.altField ); if ( tp_inst._defaults.altRedirectFocus === true ) { tp_inst.$altInput .css( { cursor: 'pointer' } ) .on( 'focus', function() { $input.trigger( 'focus' ); } ); } } if ( tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0 ) { tp_inst._defaults.minDate = new Date(); } if ( tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0 ) { tp_inst._defaults.maxDate = new Date(); } // tm_datepicker needs minDate/maxDate, tm_timepicker needs minDateTime/maxDateTime.. if ( tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date ) { tp_inst._defaults.minDateTime = new Date( tp_inst._defaults.minDate.getTime() ); } if ( tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date ) { tp_inst._defaults.minDate = new Date( tp_inst._defaults.minDateTime.getTime() ); } if ( tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date ) { tp_inst._defaults.maxDateTime = new Date( tp_inst._defaults.maxDate.getTime() ); } if ( tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date ) { tp_inst._defaults.maxDate = new Date( tp_inst._defaults.maxDateTime.getTime() ); } tp_inst.$input.on( 'focus', function() { tp_inst._onFocus(); } ); tp_inst.$input.next( '.ui-tm-timepicker-trigger' ).on( 'click', function() { tp_inst._onFocus(); /*if ( $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput === input[ 0 ] ) { $.tm_datepicker._hideDatepicker(); } else if ( $.tm_datepicker._datepickerShowing && $.tm_datepicker._lastInput !== input[ 0 ] ) { $.tm_datepicker._hideDatepicker(); $.tm_datepicker._showDatepicker( input[ 0 ] ); } else { $.tm_datepicker._showDatepicker( input[ 0 ] ); }*/ return false; } ); return tp_inst; }, /* * add our sliders to the calendar */ _addTimePicker: function( dp_inst ) { var currDT = $.epoAPI.util.trim( this.$altInput && this._defaults.altFieldTimeOnly ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val() ); this.timeDefined = this._parseTime( currDT ); this._limitMinMaxDateTime( dp_inst, false ); this._injectTimePicker(); this._afterInject(); }, /* * parse the time string from input value or _setTime */ _parseTime: function( timeString, withDate ) { var dp_dateFormat; var parseRes; var timeObj; if ( ! this.inst ) { this.inst = $.tm_datepicker._getInst( this.$input[ 0 ] ); } if ( withDate || ! this._defaults.timeOnly ) { dp_dateFormat = $.tm_datepicker._get( this.inst, 'dateFormat' ); try { parseRes = parseDateTimeInternal( dp_dateFormat, this._defaults.timeFormat, timeString, $.tm_datepicker._getFormatConfig( this.inst ), this._defaults ); if ( ! parseRes.timeObj ) { return false; } $.extend( this, parseRes.timeObj ); } catch ( err ) { $.tm_timepicker.log( 'Error parsing the date/time string: ' + err + '\ndate/time string = ' + timeString + '\ntimeFormat = ' + this._defaults.timeFormat + '\ndateFormat = ' + dp_dateFormat ); return false; } return true; } timeObj = $.tm_datepicker.parseTime( this._defaults.timeFormat, timeString, this._defaults ); if ( ! timeObj ) { return false; } $.extend( this, timeObj ); return true; }, /* * Handle callback option after injecting tm_timepicker */ _afterInject: function() { var o = this.inst.settings; if ( typeof o.afterInject === 'function' ) { o.afterInject.call( this ); } }, _ui_tpicker_onclick: function( event ) { var $t = $( event.target ), h = $t.html(), n = parseInt( h.replace( /[^0-9]/g ), 10 ), ap = h.replace( /[^apm]/gi ), f = $t.data( 'for' ); // loses scope, so we use data-for if ( f === 'hour' ) { if ( ap.indexOf( 'p' ) !== -1 && n < 12 ) { n += 12; } else if ( ap.indexOf( 'a' ) !== -1 && n === 12 ) { n = 0; } } event.data.tp_inst.control.value( event.data.tp_inst, event.data.tp_inst[ f + '_slider' ], event.data.litem, n ); event.data.tp_inst._onTimeChange(); event.data.tp_inst._onSelectHandler(); }, /* * generate and inject html for tm_timepicker into ui tm_datepicker */ _injectTimePicker: function() { var $dp = this.inst.dpDiv, o = this.inst.settings, tp_inst = this, litem = '', uitem = '', show = null, max = {}, gridSize = {}, size = null, i = 0, l = 0; var noDisplay; var html; var h; var tmph; var m; var showTz; var $tp; var local_timezone; var $buttonPanel; var timeDefined; var sliderAccessArgs; var rtl; // Prevent displaying twice if ( $dp.find( 'div.ui-tm-timepicker-div' ).length === 0 && o.showTimepicker ) { noDisplay = ' ui_tpicker_unit_hide'; html = '
' + '
' + o.timeText + '
' + '
'; // Create the markup for ( i = 0, l = this.units.length; i < l; i += 1 ) { litem = this.units[ i ]; uitem = litem.substring( 0, 1 ).toUpperCase() + litem.substring( 1 ); show = o[ 'show' + uitem ] !== null ? o[ 'show' + uitem ] : this.support[ litem ]; // Added by Peter Medeiros: // - Figure out what the hour/minute/second max should be based on the step values. // - Example: if stepMinute is 15, then minMax is 45. max[ litem ] = parseInt( o[ litem + 'Max' ] - ( ( o[ litem + 'Max' ] - o[ litem + 'Min' ] ) % o[ 'step' + uitem ] ), 10 ); gridSize[ litem ] = 0; html += '
' + o[ litem + 'Text' ] + '
' + '
'; if ( show && o[ litem + 'Grid' ] > 0 ) { html += '
'; if ( litem === 'hour' ) { for ( h = o[ litem + 'Min' ]; h <= max[ litem ]; h += parseInt( o[ litem + 'Grid' ], 10 ) ) { gridSize[ litem ] += 1; tmph = $.tm_datepicker.formatTime( this.support.ampm ? 'hht' : 'HH', { hour: h }, o ); html += ''; } } else { for ( m = o[ litem + 'Min' ]; m <= max[ litem ]; m += parseInt( o[ litem + 'Grid' ], 10 ) ) { gridSize[ litem ] += 1; html += ''; } } html += '
' + tmph + '' + ( m < 10 ? '0' : '' ) + m + '
'; } html += '
'; } // Timezone showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone; html += '
' + o.timezoneText + '
'; html += '
'; // Create the elements from string html += '
'; $tp = $( html ); // if we only want time picker... if ( o.timeOnly === true ) { $tp.prepend( '
' + '
' + o.timeOnlyTitle + '
' + '
' ); $dp.find( '.tm-ui-dp-inner-container' ).hide(); } // add sliders, adjust grids, add events for ( i = 0, l = tp_inst.units.length; i < l; i += 1 ) { litem = tp_inst.units[ i ]; uitem = litem.substring( 0, 1 ).toUpperCase() + litem.substring( 1 ); show = o[ 'show' + uitem ] !== null ? o[ 'show' + uitem ] : this.support[ litem ]; // add the slider tp_inst[ litem + '_slider' ] = tp_inst.control.create( tp_inst, $tp.find( '.ui_tpicker_' + litem + '_slider' ), litem, tp_inst[ litem ], o[ litem + 'Min' ], max[ litem ], o[ 'step' + uitem ] ); // adjust the grid and add click event if ( show && o[ litem + 'Grid' ] > 0 ) { size = ( 100 * gridSize[ litem ] * o[ litem + 'Grid' ] ) / ( max[ litem ] - o[ litem + 'Min' ] ); $tp.find( '.ui_tpicker_' + litem + ' table' ) .css( { width: size + '%', marginLeft: o.isRTL ? '0' : ( size / ( -2 * gridSize[ litem ] ) ) + '%', marginRight: o.isRTL ? ( size / ( -2 * gridSize[ litem ] ) ) + '%' : '0', borderCollapse: 'collapse' } ) .find( 'td' ) .on( 'click', { that: this, tp_inst: tp_inst, litem: litem }, this._ui_tpicker_onclick ) .css( { cursor: 'pointer', width: ( 100 / gridSize[ litem ] ) + '%', textAlign: 'center', overflow: 'hidden' } ); } // end if grid > 0 } // end for loop // Add timezone options this.timezone_select = $tp.find( '.ui_tpicker_timezone' ).append( '' ).find( 'select' ); $.fn.append.apply( this.timezone_select, $.map( o.timezoneList, function( val ) { return $( '