diff --git a/application/logs/log-2018-01-27.php b/application/logs/log-2018-01-27.php new file mode 100644 index 00000000..240a0224 --- /dev/null +++ b/application/logs/log-2018-01-27.php @@ -0,0 +1,4 @@ + + +ERROR - 2018-01-27 12:50:02 --> Severity: Notice --> Undefined offset: 1 C:\5.6\xampp\htdocs\SIA\SIANEW\current\siddharth_application\application\views\dailyBatchCardListing.php 366 +ERROR - 2018-01-27 12:50:02 --> Severity: Notice --> Undefined offset: 1 C:\5.6\xampp\htdocs\SIA\SIANEW\current\siddharth_application\application\views\dailyBatchCardListing.php 366 diff --git a/application/views/dailyBatchCardListing.php b/application/views/dailyBatchCardListing.php index 5ff82689..7b783df8 100755 --- a/application/views/dailyBatchCardListing.php +++ b/application/views/dailyBatchCardListing.php @@ -235,6 +235,9 @@ td{ padding:3px;} +
+ Add New Daily Data +
diff --git a/application/views/dailybatchcard.php b/application/views/dailybatchcard.php index 91280274..73de1a16 100755 --- a/application/views/dailybatchcard.php +++ b/application/views/dailybatchcard.php @@ -111,7 +111,7 @@ foreach($emplist as $r)
 
-
+
Ed Details
@@ -128,7 +128,7 @@ foreach($emplist as $r)
-
+
Rotary Details
@@ -146,7 +146,7 @@ foreach($emplist as $r)
 
-
+
TRP Rough Sand
diff --git a/application/views/loanreports.php b/application/views/loanreports.php index 315ceacf..e63c6a8d 100755 --- a/application/views/loanreports.php +++ b/application/views/loanreports.php @@ -36,19 +36,32 @@ // echo $r->Due_Start_Date; $rdues = $r->No_of_Dues; $time=strtotime($r->Due_Start_Date); - // echo date('Y-m-d',$time); + // echo date('Y-m-d',$time).'
'; + //echo 'NO of Dues -' .$rdues.'
'; $dueendyear=date("Y",$time); $dueendmonth=date("m",$time); $dueenddate=date("d",$time); + //echo 'end month -' .$dueendmonth.'
'; $dueendmonth = $dueendmonth + ($rdues - 1); - + //echo 'afetr end month -' .$dueendmonth.'
'; + //echo $dueendyear.'-'.$dueendmonth.'-'.$dueenddate.'
'; + if($dueendmonth > 12){ + $dueendmonth = ($dueendmonth - 12); + } + if($dueendmonth < 10){ $dueendmonth = '0'.$dueendmonth; } - // echo $dueendmonth; + if($dueendyear < date('Y')) + { + $dueendyear = date('Y'); + + } + + //echo $dueendmonth.'
'; $finaldueenddate = $dueendyear.'-'.$dueendmonth.'-'.$dueenddate; $curdate = date('Y-m-d'); - // echo $curdate.'-'.$finaldueenddate;exit(); + //echo $curdate.'-'.$finaldueenddate; if($curdate > $finaldueenddate) { diff --git a/assets/jquery.plugin.js b/assets/jquery.plugin.js new file mode 100644 index 00000000..50470fe8 --- /dev/null +++ b/assets/jquery.plugin.js @@ -0,0 +1,344 @@ +/* Simple JavaScript Inheritance + * By John Resig http://ejohn.org/ + * MIT Licensed. + */ +// Inspired by base2 and Prototype +(function(){ + var initializing = false; + + // The base JQClass implementation (does nothing) + window.JQClass = function(){}; + + // Collection of derived classes + JQClass.classes = {}; + + // Create a new JQClass that inherits from this class + JQClass.extend = function extender(prop) { + var base = this.prototype; + + // Instantiate a base class (but only create the instance, + // don't run the init constructor) + initializing = true; + var prototype = new this(); + initializing = false; + + // Copy the properties over onto the new prototype + for (var name in prop) { + // Check if we're overwriting an existing function + prototype[name] = typeof prop[name] == 'function' && + typeof base[name] == 'function' ? + (function(name, fn){ + return function() { + var __super = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = function(args) { + return base[name].apply(this, args || []); + }; + + var ret = fn.apply(this, arguments); + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + this._super = __super; + + return ret; + }; + })(name, prop[name]) : + prop[name]; + } + + // The dummy class constructor + function JQClass() { + // All construction is actually done in the init method + if (!initializing && this._init) { + this._init.apply(this, arguments); + } + } + + // Populate our constructed prototype object + JQClass.prototype = prototype; + + // Enforce the constructor to be what we expect + JQClass.prototype.constructor = JQClass; + + // And make this class extendable + JQClass.extend = extender; + + return JQClass; + }; +})(); + +(function($) { // Ensure $, encapsulate + + /** Abstract base class for collection plugins v1.0.1. + Written by Keith Wood (kbwood{at}iinet.com.au) December 2013. + Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license. + @module $.JQPlugin + @abstract */ + JQClass.classes.JQPlugin = JQClass.extend({ + + /** Name to identify this plugin. + @example name: 'tabs' */ + name: 'plugin', + + /** Default options for instances of this plugin (default: {}). + @example defaultOptions: { + selectedClass: 'selected', + triggers: 'click' + } */ + defaultOptions: {}, + + /** Options dependent on the locale. + Indexed by language and (optional) country code, with '' denoting the default language (English/US). + @example regionalOptions: { + '': { + greeting: 'Hi' + } + } */ + regionalOptions: {}, + + /** Names of getter methods - those that can't be chained (default: []). + @example _getters: ['activeTab'] */ + _getters: [], + + /** Retrieve a marker class for affected elements. + @private + @return {string} The marker class. */ + _getMarker: function() { + return 'is-' + this.name; + }, + + /** Initialise the plugin. + Create the jQuery bridge - plugin name xyz + produces $.xyz and $.fn.xyz. */ + _init: function() { + // Apply default localisations + $.extend(this.defaultOptions, (this.regionalOptions && this.regionalOptions['']) || {}); + // Camel-case the name + var jqName = camelCase(this.name); + // Expose jQuery singleton manager + $[jqName] = this; + // Expose jQuery collection plugin + $.fn[jqName] = function(options) { + var otherArgs = Array.prototype.slice.call(arguments, 1); + if ($[jqName]._isNotChained(options, otherArgs)) { + return $[jqName][options].apply($[jqName], [this[0]].concat(otherArgs)); + } + return this.each(function() { + if (typeof options === 'string') { + if (options[0] === '_' || !$[jqName][options]) { + throw 'Unknown method: ' + options; + } + $[jqName][options].apply($[jqName], [this].concat(otherArgs)); + } + else { + $[jqName]._attach(this, options); + } + }); + }; + }, + + /** Set default values for all subsequent instances. + @param options {object} The new default options. + @example $.plugin.setDefauls({name: value}) */ + setDefaults: function(options) { + $.extend(this.defaultOptions, options || {}); + }, + + /** Determine whether a method is a getter and doesn't permit chaining. + @private + @param name {string} The method name. + @param otherArgs {any[]} Any other arguments for the method. + @return {boolean} True if this method is a getter, false otherwise. */ + _isNotChained: function(name, otherArgs) { + if (name === 'option' && (otherArgs.length === 0 || + (otherArgs.length === 1 && typeof otherArgs[0] === 'string'))) { + return true; + } + return $.inArray(name, this._getters) > -1; + }, + + /** Initialise an element. Called internally only. + Adds an instance object as data named for the plugin. + @param elem {Element} The element to enhance. + @param options {object} Overriding settings. */ + _attach: function(elem, options) { + elem = $(elem); + if (elem.hasClass(this._getMarker())) { + return; + } + elem.addClass(this._getMarker()); + options = $.extend({}, this.defaultOptions, this._getMetadata(elem), options || {}); + var inst = $.extend({name: this.name, elem: elem, options: options}, + this._instSettings(elem, options)); + elem.data(this.name, inst); // Save instance against element + this._postAttach(elem, inst); + this.option(elem, options); + }, + + /** Retrieve additional instance settings. + Override this in a sub-class to provide extra settings. + @param elem {jQuery} The current jQuery element. + @param options {object} The instance options. + @return {object} Any extra instance values. + @example _instSettings: function(elem, options) { + return {nav: elem.find(options.navSelector)}; + } */ + _instSettings: function(elem, options) { + return {}; + }, + + /** Plugin specific post initialisation. + Override this in a sub-class to perform extra activities. + @param elem {jQuery} The current jQuery element. + @param inst {object} The instance settings. + @example _postAttach: function(elem, inst) { + elem.on('click.' + this.name, function() { + ... + }); + } */ + _postAttach: function(elem, inst) { + }, + + /** Retrieve metadata configuration from the element. + Metadata is specified as an attribute: + data-<plugin name>="<setting name>: '<value>', ...". + Dates should be specified as strings in this format: 'new Date(y, m-1, d)'. + @private + @param elem {jQuery} The source element. + @return {object} The inline configuration or {}. */ + _getMetadata: function(elem) { + try { + var data = elem.data(this.name.toLowerCase()) || ''; + data = data.replace(/'/g, '"'); + data = data.replace(/([a-zA-Z0-9]+):/g, function(match, group, i) { + var count = data.substring(0, i).match(/"/g); // Handle embedded ':' + return (!count || count.length % 2 === 0 ? '"' + group + '":' : group + ':'); + }); + data = $.parseJSON('{' + data + '}'); + for (var name in data) { // Convert dates + var value = data[name]; + if (typeof value === 'string' && value.match(/^new Date\((.*)\)$/)) { + data[name] = eval(value); + } + } + return data; + } + catch (e) { + return {}; + } + }, + + /** Retrieve the instance data for element. + @param elem {Element} The source element. + @return {object} The instance data or {}. */ + _getInst: function(elem) { + return $(elem).data(this.name) || {}; + }, + + /** Retrieve or reconfigure the settings for a plugin. + @param elem {Element} The source element. + @param name {object|string} The collection of new option values or the name of a single option. + @param [value] {any} The value for a single named option. + @return {any|object} If retrieving a single value or all options. + @example $(selector).plugin('option', 'name', value) + $(selector).plugin('option', {name: value, ...}) + var value = $(selector).plugin('option', 'name') + var options = $(selector).plugin('option') */ + option: function(elem, name, value) { + elem = $(elem); + var inst = elem.data(this.name); + if (!name || (typeof name === 'string' && value == null)) { + var options = (inst || {}).options; + return (options && name ? options[name] : options); + } + if (!elem.hasClass(this._getMarker())) { + return; + } + var options = name || {}; + if (typeof name === 'string') { + options = {}; + options[name] = value; + } + this._optionsChanged(elem, inst, options); + $.extend(inst.options, options); + }, + + /** Plugin specific options processing. + Old value available in inst.options[name], new value in options[name]. + Override this in a sub-class to perform extra activities. + @param elem {jQuery} The current jQuery element. + @param inst {object} The instance settings. + @param options {object} The new options. + @example _optionsChanged: function(elem, inst, options) { + if (options.name != inst.options.name) { + elem.removeClass(inst.options.name).addClass(options.name); + } + } */ + _optionsChanged: function(elem, inst, options) { + }, + + /** Remove all trace of the plugin. + Override _preDestroy for plugin-specific processing. + @param elem {Element} The source element. + @example $(selector).plugin('destroy') */ + destroy: function(elem) { + elem = $(elem); + if (!elem.hasClass(this._getMarker())) { + return; + } + this._preDestroy(elem, this._getInst(elem)); + elem.removeData(this.name).removeClass(this._getMarker()); + }, + + /** Plugin specific pre destruction. + Override this in a sub-class to perform extra activities and undo everything that was + done in the _postAttach or _optionsChanged functions. + @param elem {jQuery} The current jQuery element. + @param inst {object} The instance settings. + @example _preDestroy: function(elem, inst) { + elem.off('.' + this.name); + } */ + _preDestroy: function(elem, inst) { + } + }); + + /** Convert names from hyphenated to camel-case. + @private + @param value {string} The original hyphenated name. + @return {string} The camel-case version. */ + function camelCase(name) { + return name.replace(/-([a-z])/g, function(match, group) { + return group.toUpperCase(); + }); + } + + /** Expose the plugin base. + @namespace "$.JQPlugin" */ + $.JQPlugin = { + + /** Create a new collection plugin. + @memberof "$.JQPlugin" + @param [superClass='JQPlugin'] {string} The name of the parent class to inherit from. + @param overrides {object} The property/function overrides for the new class. + @example $.JQPlugin.createPlugin({ + name: 'tabs', + defaultOptions: {selectedClass: 'selected'}, + _initSettings: function(elem, options) { return {...}; }, + _postAttach: function(elem, inst) { ... } + }); */ + createPlugin: function(superClass, overrides) { + if (typeof superClass === 'object') { + overrides = superClass; + superClass = 'JQPlugin'; + } + superClass = camelCase(superClass); + var className = camelCase(overrides.name); + JQClass.classes[className] = JQClass.classes[superClass].extend(overrides); + new JQClass.classes[className](); + } + }; + +})(jQuery); \ No newline at end of file diff --git a/assets/jquery.timeentry.js b/assets/jquery.timeentry.js new file mode 100644 index 00000000..d48a6478 --- /dev/null +++ b/assets/jquery.timeentry.js @@ -0,0 +1,1059 @@ +/* http://keith-wood.name/timeEntry.html + Time entry for jQuery v2.0.1. + Written by Keith Wood (kbwood{at}iinet.com.au) June 2007. + Available under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license. + Please attribute the author if you use it. */ + +(function($) { // Hide scope, no $ conflict + + var pluginName = 'timeEntry'; + + /** Create the time entry plugin. +

Sets an input field to add a spinner for time entry.

+

The time can be entered via directly typing the value, + via the arrow keys, or via spinner buttons. + It is configurable to show 12 or 24-hour time, to show or hide seconds, + to enforce a minimum and/or maximum time, to change the spinner image, + and to constrain the time to steps, e.g. only on the quarter hours.

+

Expects HTML like:

+
<input type="text">
+

Provide inline configuration like:

+
<input type="text" data-timeEntry="name: 'value'">
+ @module TimeEntry + @augments JQPlugin + @example $(selector).timeEntry() + $(selector).timeEntry({showSeconds: true, minTime: new Date(0, 0, 0, 12, 0, 0)}) */ + $.JQPlugin.createPlugin({ + + /** The name of the plugin. */ + name: pluginName, + + /** Time entry before show callback. + Triggered when the input field is focussed. + @callback beforeShowCallback + @param input {Element} The current input field. + @return {object} Any changes to the instance settings. + @example beforeShow: function(input) { + // Cross-populate minimum/maximum times for a range + return {minTime: (input.id === 'timeTo' ? + $('#timeFrom').timeEntry('getTime') : null), + maxTime: (input.id === 'timeFrom' ? + $('#timeTo').timeEntry('getTime') : null)}; + } */ + + /** Time entry before set time callback. + Triggered when the input field value is to be changed. + @callback beforeSetTimeCallback + @param current {string} The current time value entered. + @param newTime {string} The new time value to use. + @param minTime {Date} The minimum time value allowed. + @param maxTime {Date} The maximum time value allowed. + @return {Date} The actual time value to set. + @example beforeSetTime: function(oldTime, newTime, minTime, maxTime) { + var increment = (newTime - (oldTime || newTime)) > 0; + if (newTime.getMinutes() > 30) { // First half of hour only + newTime.setMinutes(increment ? 0 : 30); + newTime.setHours(newTime.getHours() + (increment ? 1 : 0)); + } + return newTime; + } */ + + /** Default settings for the plugin. + @property [appendText=''] {string} Display text following the input box, e.g. showing the format. + @property [showSeconds=false] {boolean} true to show seconds as well, + false for hours/minutes only. + @property [unlimitedHours=false] {boolean} true to allow entry of more than 24 hours, + false to restrict to one day. + @property [timeSteps=[1,1,1]] {number[]} Steps for each of hours/minutes/seconds when incrementing/decrementing. + @property [initialField=null] {number} The field to highlight initially (0 = hours, 1 = minutes, ...), + or null for none. + @property [noSeparatorEntry=false] {boolean} true to move to next sub-field after two digits entry. + @property [tabToExit=false] {boolean} true for tab key to go to next element, + false for tab key to step through internal fields. + @property [useMouseWheel=true] {boolean} true to use mouse wheel for increment/decrement if possible, + false to never use it. + @property [defaultTime=null] {Date|number|string} The time to use if none has been set, + or null for now. Specify as a Date object, as a number of seconds + offset from now, or as a string of offsets from now, using 'H' for hours, + 'M' for minutes, 'S' for seconds. + @property [minTime=null] {Date|number|string|number[]} The earliest selectable time, + or null for no limit. See defaultTime for possible formats, + use array of hours, minutes, seconds for unlimitedHours. + @property [maxTime=null] {Date|number|string|number[]} The latest selectable time, + or null for no limit. See defaultTime for possible formats, + use array of hours, minutes, seconds for unlimitedHours. + @property [spinnerImage='spinnerDefault.png'] {string} The URL of the images to use for the time spinner - + seven images packed horizontally for normal, each button pressed + (centre, previous, next, increment, decrement), and disabled. + @property [spinnerSize=[20,20,8]] {number[]} The width and height of the spinner image, + and size of centre button for current time. + @property [spinnerBigImage=''] {string} The URL of the images to use for the expanded time spinner - + seven images packed horizontally for normal, each button pressed + (centre, previous, next, increment, decrement), and disabled. + @property [spinnerBigSize=[40,40,16]] {number[]} The width and height of the expanded spinner image, + and size of centre button for current time. + @property [spinnerIncDecOnly=false] {boolean} true for increment/decrement buttons only, false for all. + @property [spinnerRepeat=[500,250]] {number[]} Initial and subsequent waits in milliseconds + for repeats on the spinner buttons. + @property [beforeShow=null] {beforeShowCallback} Function that takes an input field and + returns a set of custom settings for the time entry. + @property [beforeSetTime=null] {beforeSetTimeCallback} Function that runs before updating the time, + takes the old and new times, and minimum and maximum times as parameters, + and returns an adjusted time if necessary. + @example {defaultTime: new Date(0, 0, 0, 8, 30, 0), minTime: -300, maxTime: '+2H +30M'} */ + defaultOptions: { + appendText: '', + showSeconds: false, + unlimitedHours: false, + timeSteps: [1, 1, 1], + initialField: null, + noSeparatorEntry: false, + tabToExit: false, + useMouseWheel: true, + defaultTime: null, + minTime: null, + maxTime: null, + spinnerImage: 'spinnerDefault.png', + spinnerSize: [20, 20, 8], + spinnerBigImage: '', + spinnerBigSize: [40, 40, 16], + spinnerIncDecOnly: false, + spinnerRepeat: [500, 250], + beforeShow: null, + beforeSetTime: null + }, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @property [show24Hours=false] {boolean} true to use 24 hour time, false for 12 hour (AM/PM). + @property [separator=':'] {string} The separator between time fields. + @property [ampmPrefix=''] {string} The separator before the AM/PM text. + @property [ampmNames=['AM','PM']] {string[]} Names of morning/evening markers. + @property [spinnerTexts=['Now','Previous field','Next field','Increment','Decrement']] {string[]} + The popup texts for the spinner image areas. */ + regionalOptions: { // Available regional settings, indexed by language/country code + '': { // Default regional settings - English/US + show24Hours: false, + separator: ':', + ampmPrefix: '', + ampmNames: ['AM', 'PM'], + spinnerTexts: ['Now', 'Previous field', 'Next field', 'Increment', 'Decrement'] + } + }, + + _getters: ['getOffset', 'getTime', 'isDisabled'], + + _appendClass: pluginName + '-append', // Class name for the appended content + _controlClass: pluginName + '-control', // Class name for the date entry control + _expandClass: pluginName + '-expand', // Class name for the expanded spinner + + _disabledInputs: [], // List of time inputs that have been disabled + + _instSettings: function(elem, options) { + return {_field: 0, _selectedHour: 0, _selectedMinute: 0, _selectedSecond: 0}; + }, + + _postAttach: function(elem, inst) { + elem.on('focus.' + inst.name, this._doFocus). + on('blur.' + inst.name, this._doBlur). + on('click.' + inst.name, this._doClick). + on('keydown.' + inst.name, this._doKeyDown). + on('keypress.' + inst.name, this._doKeyPress). + on('paste.' + inst.name, function(event) { // Check pastes + setTimeout(function() { plugin._parseTime(inst); }, 1); + }); + }, + + _optionsChanged: function(elem, inst, options) { + var currentTime = this._extractTime(inst); + $.extend(inst.options, options); + inst.options.show24Hours = inst.options.show24Hours || inst.options.unlimitedHours; + inst._field = 0; + if (currentTime) { + this._setTime(inst, new Date(0, 0, 0, currentTime[0], currentTime[1], currentTime[2])); + } + // Remove stuff dependent on old settings + elem.next('span.' + this._appendClass).remove(); + elem.parent().find('span.' + this._controlClass).remove(); + if ($.fn.mousewheel) { + elem.unmousewheel(); + } + // And re-add if requested + var spinner = (!inst.options.spinnerImage ? null : + $('')); + elem.after(inst.options.appendText ? '' + + inst.options.appendText + '' : '').after(spinner || ''); + // Allow mouse wheel usage + if (inst.options.useMouseWheel && $.fn.mousewheel) { + elem.mousewheel(this._doMouseWheel); + } + if (spinner) { + spinner.mousedown(this._handleSpinner).mouseup(this._endSpinner). + mouseover(this._expandSpinner).mouseout(this._endSpinner). + mousemove(this._describeSpinner); + } + }, + + /** Enable a time entry input and any associated spinner. + @param elem {Element} The single input field. + @example $(selector).timeEntry('enable') */ + enable: function(elem) { + this._enableDisable(elem, false); + }, + + /** Disable a time entry input and any associated spinner. + @param elem {Element} The single input field. + @example $(selector).timeEntry('disable') */ + disable: function(elem) { + this._enableDisable(elem, true); + }, + + /** Enable or disable a time entry input and any associated spinner. + @private + @param elem {Element} The single input field. + @param disable {boolean} true to disable, false to enable. */ + _enableDisable: function(elem, disable) { + var inst = this._getInst(elem); + if (!inst) { + return; + } + elem.disabled = disable; + if (elem.nextSibling && elem.nextSibling.nodeName.toLowerCase() === 'span') { + this._changeSpinner(inst, elem.nextSibling, (disable ? 5 : -1)); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === elem ? null : value); }); // Delete entry + if (disable) { + this._disabledInputs.push(elem); + } + }, + + /** Check whether an input field has been disabled. + @param elem {Element} The input field to check. + @return {boolean} true if this field has been disabled, false if it is enabled. + @example if ($(selector).dateEntry('isDisabled')) {...} */ + isDisabled: function(elem) { + return $.inArray(elem, this._disabledInputs) > -1; + }, + + _preDestroy: function(elem, inst) { + elem = $(elem).off('.' + pluginName); + if ($.fn.mousewheel) { + elem.unmousewheel(); + } + this._disabledInputs = $.map(this._disabledInputs, + function(value) { return (value === elem[0] ? null : value); }); // Delete entry + elem.siblings('.' + this._appendClass + ',.' + this._controlClass).remove(); + }, + + /** Initialise the current time for a time entry input field. + @param elem {Element} The input field to update. + @param time {Date|number|string} The new time or offset or null to clear. + An actual time or offset in seconds from now or units and periods of offsets from now. + @example $(selector).timeEntry('setTime', new Date(0, 0, 0, 11, 22, 33)) + $(selector).timeEntry('setTime', +300) + $(selector).timeEntry('setTime', '+1H +30M') + $(selector).timeEntry('setTime', null) */ + setTime: function(elem, time) { + var inst = this._getInst(elem); + if (inst) { + if (time === null || time === '') { + $(elem).val(''); + } + else { + this._setTime(inst, time ? ($.isArray(time) ? time : + (typeof time === 'object' ? new Date(time.getTime()) : time)) : null); + } + } + }, + + /** Retrieve the current time for a time entry input field. + @param elem {Element} The input field to update. + @return {Date} The current time or null if none. + @example var time = $(selector).timeEntry('getTime') */ + getTime: function(elem) { + var inst = this._getInst(elem); + var currentTime = (inst ? this._extractTime(inst) : null); + return (!currentTime ? null : + new Date(0, 0, 0, currentTime[0], currentTime[1], currentTime[2])); + }, + + /** Retrieve the millisecond offset for the current time. + @param elem {Element} The input field to examine. + @return {number} The time as milliseconds offset or zero if none. + @example var offset = $(selector).timeEntry('getOffset') */ + getOffset: function(elem) { + var inst = this._getInst(elem); + var currentTime = (inst ? this._extractTime(inst) : null); + return (!currentTime ? 0 : + (currentTime[0] * 3600 + currentTime[1] * 60 + currentTime[2]) * 1000); + }, + + /** Initialise date entry. + @private + @param elem {Element|Event} The input field or the focus event. */ + _doFocus: function(elem) { + var input = (elem.nodeName && elem.nodeName.toLowerCase() === 'input' ? elem : this); + if (plugin._lastInput === input || plugin.isDisabled(input)) { + plugin._focussed = false; + return; + } + var inst = plugin._getInst(input); + plugin._focussed = true; + plugin._lastInput = input; + plugin._blurredInput = null; + $.extend(inst.options, ($.isFunction(inst.options.beforeShow) ? + inst.options.beforeShow.apply(input, [input]) : {})); + plugin._parseTime(inst, elem.nodeName ? null : elem); + setTimeout(function() { plugin._showField(inst); }, 10); + }, + + /** Note that the field has been exited. + @private + @param event {Event} The blur event. */ + _doBlur: function(event) { + plugin._blurredInput = plugin._lastInput; + plugin._lastInput = null; + }, + + /** Select appropriate field portion on click, if already in the field. + @private + @param event {Event} The click event. */ + _doClick: function(event) { + var input = event.target; + var inst = plugin._getInst(input); + var prevField = inst._field; + if (!plugin._focussed) { + inst._field = plugin._getSelection(inst, input, event); + } + if (prevField !== inst._field) { + inst._lastChr = ''; + } + plugin._showField(inst); + plugin._focussed = false; + }, + + /** Find the selected subfield within the control. + @private + @param inst {object} The current instance settings. + @param input {Element} The input control. + @param event {Event} The triggering event. + @return {number} The selected subfield. */ + _getSelection: function(inst, input, event) { + var select = 0; + var fieldSizes = [inst.elem.val().split(inst.options.separator)[0].length, 2, 2]; + if (input.selectionStart !== null) { // Use input select range + var end = 0; + for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) { + end += (field !== inst._ampmField ? fieldSizes[field] + inst.options.separator.length : + inst.options.ampmPrefix.length + inst.options.ampmNames[0].length); + select = field; + if (input.selectionStart < end) { + break; + } + } + } + else if (input.createTextRange && event != null) { // Check against bounding boxes + var src = $(event.srcElement); + var range = input.createTextRange(); + var convert = function(value) { + return {thin: 2, medium: 4, thick: 6}[value] || value; + }; + var offsetX = event.clientX + document.documentElement.scrollLeft - + (src.offset().left + parseInt(convert(src.css('border-left-width')), 10)) - + range.offsetLeft; // Position - left edge - alignment + for (var field = 0; field <= Math.max(1, inst._secondField, inst._ampmField); field++) { + var end = (field !== inst._ampmField ? (field * fieldSize) + 2 : + (inst._ampmField * fieldSize) + inst.options.ampmPrefix.length + + inst.options.ampmNames[0].length); + range.collapse(); + range.moveEnd('character', end); + select = field; + if (offsetX < range.boundingWidth) { // And compare + break; + } + } + } + return select; + }, + + /** Handle keystrokes in the field. + @private + @param event {Event} The keydown event. + @return {boolean} true to continue, false to stop processing. */ + _doKeyDown: function(event) { + if (event.keyCode >= 48) { // >= '0' + return true; + } + var inst = plugin._getInst(event.target); + switch (event.keyCode) { + case 9: return (inst.options.tabToExit ? true : (event.shiftKey ? + // Move to previous time field, or out if at the beginning + plugin._changeField(inst, -1, true) : + // Move to next time field, or out if at the end + plugin._changeField(inst, +1, true))); + case 35: if (event.ctrlKey) { // Clear time on ctrl+end + plugin._setValue(inst, ''); + } + else { // Last field on end + inst._field = Math.max(1, inst._secondField, inst._ampmField); + plugin._adjustField(inst, 0); + } + break; + case 36: if (event.ctrlKey) { // Current time on ctrl+home + plugin._setTime(inst); + } + else { // First field on home + inst._field = 0; + plugin._adjustField(inst, 0); + } + break; + case 37: plugin._changeField(inst, -1, false); break; // Previous field on left + case 38: plugin._adjustField(inst, +1); break; // Increment time field on up + case 39: plugin._changeField(inst, +1, false); break; // Next field on right + case 40: plugin._adjustField(inst, -1); break; // Decrement time field on down + case 46: plugin._setValue(inst, ''); break; // Clear time on delete + case 8: inst._lastChr = ''; // Fall through + default: return true; + } + return false; + }, + + /** Disallow unwanted characters. + @private + @param event {Event} The keypress event. + @return {boolean} true to continue, false to stop processing. */ + _doKeyPress: function(event) { + var chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode); + if (chr < ' ') { + return true; + } + var inst = plugin._getInst(event.target); + plugin._handleKeyPress(inst, chr); + return false; + }, + + /** Update date based on keystroke entered. + @private + @param inst {object} The instance settings. + @param chr {string} The new character. */ + _handleKeyPress: function(inst, chr) { + if (chr === inst.options.separator) { + this._changeField(inst, +1, false); + } + else if (chr >= '0' && chr <= '9') { // Allow direct entry of date + var key = parseInt(chr, 10); + var value = parseInt(inst._lastChr + chr, 10); + var hour = (inst._field !== 0 ? inst._selectedHour : + (inst.options.unlimitedHours ? value : + (inst.options.show24Hours ? (value < 24 ? value : key) : + (value >= 1 && value <= 12 ? value : + (key > 0 ? key : inst._selectedHour)) % 12 + + (inst._selectedHour >= 12 ? 12 : 0)))); + var minute = (inst._field !== 1 ? inst._selectedMinute : + (value < 60 ? value : key)); + var second = (inst._field !== inst._secondField ? inst._selectedSecond : + (value < 60 ? value : key)); + var fields = this._constrainTime(inst, [hour, minute, second]); + this._setTime(inst, (inst.options.unlimitedHours ? fields : + new Date(0, 0, 0, fields[0], fields[1], fields[2]))); + if (inst.options.noSeparatorEntry && inst._lastChr) { + this._changeField(inst, +1, false); + } + else { + inst._lastChr = (inst.options.unlimitedHours && inst._field === 0 ? inst._lastChr + chr : chr); + } + } + else if (!inst.options.show24Hours) { // Set am/pm based on first char of names + chr = chr.toLowerCase(); + if ((chr === inst.options.ampmNames[0].substring(0, 1).toLowerCase() && + inst._selectedHour >= 12) || + (chr === inst.options.ampmNames[1].substring(0, 1).toLowerCase() && + inst._selectedHour < 12)) { + var saveField = inst._field; + inst._field = inst._ampmField; + this._adjustField(inst, +1); + inst._field = saveField; + this._showField(inst); + } + } + }, + + /** Increment/decrement on mouse wheel activity. + @private + @param event {Event} The mouse wheel event. + @param delta {number} The amount of change. */ + _doMouseWheel: function(event, delta) { + if (plugin.isDisabled(event.target)) { + return; + } + var inst = plugin._getInst(event.target); + inst.elem.focus(); + if (!inst.elem.val()) { + plugin._parseTime(inst); + } + plugin._adjustField(inst, delta); + event.preventDefault(); + }, + + /** Expand the spinner, if possible, to make it easier to use. + @private + @param event {Event} The mouse over event. */ + _expandSpinner: function(event) { + var spinner = plugin._getSpinnerTarget(event); + var inst = plugin._getInst(plugin._getInput(spinner)); + if (plugin.isDisabled(inst.elem[0])) { + return; + } + if (inst.options.spinnerBigImage) { + inst._expanded = true; + var offset = $(spinner).offset(); + var relative = null; + $(spinner).parents().each(function() { + var parent = $(this); + if (parent.css('position') === 'relative' || parent.css('position') === 'absolute') { + relative = parent.offset(); + } + return !relative; + }); + $('
'). + mousedown(plugin._handleSpinner).mouseup(plugin._endSpinner). + mouseout(plugin._endExpand).mousemove(plugin._describeSpinner). + insertAfter(spinner); + } + }, + + /** Locate the actual input field from the spinner. + @private + @param spinner {Element} The current spinner. + @return {Element} The corresponding input. */ + _getInput: function(spinner) { + return $(spinner).siblings('.' + this._getMarker())[0]; + }, + + /** Change the title based on position within the spinner. + @private + @param event {Event} The mouse move event. */ + _describeSpinner: function(event) { + var spinner = plugin._getSpinnerTarget(event); + var inst = plugin._getInst(plugin._getInput(spinner)); + spinner.title = inst.options.spinnerTexts[plugin._getSpinnerRegion(inst, event)]; + }, + + /** Handle a click on the spinner. + @private + @param event {Event} The mouse click event. */ + _handleSpinner: function(event) { + var spinner = plugin._getSpinnerTarget(event); + var input = plugin._getInput(spinner); + if (plugin.isDisabled(input)) { + return; + } + if (input === plugin._blurredInput) { + plugin._lastInput = input; + plugin._blurredInput = null; + } + var inst = plugin._getInst(input); + plugin._doFocus(input); + var region = plugin._getSpinnerRegion(inst, event); + plugin._changeSpinner(inst, spinner, region); + plugin._actionSpinner(inst, region); + plugin._timer = null; + plugin._handlingSpinner = true; + if (region >= 3 && inst.options.spinnerRepeat[0]) { // Repeat increment/decrement + plugin._timer = setTimeout( + function() { plugin._repeatSpinner(inst, region); }, + inst.options.spinnerRepeat[0]); + $(spinner).one('mouseout', plugin._releaseSpinner). + one('mouseup', plugin._releaseSpinner); + } + }, + + /** Action a click on the spinner. + @private + @param inst {object} The instance settings. + @param region {number} The spinner "button". */ + _actionSpinner: function(inst, region) { + if (!inst.elem.val()) { + plugin._parseTime(inst); + } + switch (region) { + case 0: this._setTime(inst); break; + case 1: this._changeField(inst, -1, false); break; + case 2: this._changeField(inst, +1, false); break; + case 3: this._adjustField(inst, +1); break; + case 4: this._adjustField(inst, -1); break; + } + }, + + /** Repeat a click on the spinner. + @private + @param inst {object} The instance settings. + @param region {number} The spinner "button". */ + _repeatSpinner: function(inst, region) { + if (!plugin._timer) { + return; + } + plugin._lastInput = plugin._blurredInput; + this._actionSpinner(inst, region); + this._timer = setTimeout( + function() { plugin._repeatSpinner(inst, region); }, + inst.options.spinnerRepeat[1]); + }, + + /** Stop a spinner repeat. + @private + @param event {Event} The mouse event. */ + _releaseSpinner: function(event) { + clearTimeout(plugin._timer); + plugin._timer = null; + }, + + /** Tidy up after an expanded spinner. + @private + @param event {Event} The mouse event. */ + _endExpand: function(event) { + plugin._timer = null; + var spinner = plugin._getSpinnerTarget(event); + var input = plugin._getInput(spinner); + var inst = plugin._getInst(input); + $(spinner).remove(); + inst._expanded = false; + }, + + /** Tidy up after a spinner click. + @private + @param event {Event} The mouse event. */ + _endSpinner: function(event) { + plugin._timer = null; + var spinner = plugin._getSpinnerTarget(event); + var input = plugin._getInput(spinner); + var inst = plugin._getInst(input); + if (!plugin.isDisabled(input)) { + plugin._changeSpinner(inst, spinner, -1); + } + if (plugin._handlingSpinner) { + plugin._lastInput = plugin._blurredInput; + } + if (plugin._lastInput && plugin._handlingSpinner) { + plugin._showField(inst); + } + plugin._handlingSpinner = false; + }, + + /** Retrieve the spinner from the event. + @private + @param event {Event} The mouse click event. + @return {Element} The target field. */ + _getSpinnerTarget: function(event) { + return event.target || event.srcElement; + }, + + /** Determine which "button" within the spinner was clicked. + @private + @param inst {object} The instance settings. + @param event {Event} The mouse event. + @return {number} The spinner "button" number. */ + _getSpinnerRegion: function(inst, event) { + var spinner = this._getSpinnerTarget(event); + var pos = $(spinner).offset(); + var scrolled = [document.documentElement.scrollLeft || document.body.scrollLeft, + document.documentElement.scrollTop || document.body.scrollTop]; + var left = (inst.options.spinnerIncDecOnly ? 99 : event.clientX + scrolled[0] - pos.left); + var top = event.clientY + scrolled[1] - pos.top; + var spinnerSize = inst.options[inst._expanded ? 'spinnerBigSize' : 'spinnerSize']; + var right = (inst.options.spinnerIncDecOnly ? 99 : spinnerSize[0] - 1 - left); + var bottom = spinnerSize[1] - 1 - top; + if (spinnerSize[2] > 0 && Math.abs(left - right) <= spinnerSize[2] && + Math.abs(top - bottom) <= spinnerSize[2]) { + return 0; // Centre button + } + var min = Math.min(left, top, right, bottom); + return (min === left ? 1 : (min === right ? 2 : (min === top ? 3 : 4))); // Nearest edge + }, + + /** Change the spinner image depending on the button clicked. + @private + @param inst {object} The instance settings. + @param spinner {Element} The spinner control. + @param region {number} The spinner "button". */ + _changeSpinner: function(inst, spinner, region) { + $(spinner).css('background-position', '-' + ((region + 1) * + inst.options[inst._expanded ? 'spinnerBigSize' : 'spinnerSize'][0]) + 'px 0px'); + }, + + /** Extract the time value from the input field, or default to now. + @private + @param inst {object} The instance settings. + @param event {Event} The triggering event or null. */ + _parseTime: function(inst, event) { + var currentTime = this._extractTime(inst); + if (currentTime) { + inst._selectedHour = currentTime[0]; + inst._selectedMinute = currentTime[1]; + inst._selectedSecond = currentTime[2]; + } + else { + var now = this._constrainTime(inst); + inst._selectedHour = now[0]; + inst._selectedMinute = now[1]; + inst._selectedSecond = (inst.options.showSeconds ? now[2] : 0); + } + inst._secondField = (inst.options.showSeconds ? 2 : -1); + inst._ampmField = (inst.options.show24Hours ? -1 : (inst.options.showSeconds ? 3 : 2)); + inst._lastChr = ''; + var postProcess = function() { + if (inst.elem.val() !== '') { + plugin._showTime(inst); + } + }; + if (typeof inst.options.initialField === 'number') { + inst._field = Math.max(0, Math.min( + Math.max(1, inst._secondField, inst._ampmField), inst.options.initialField)); + postProcess(); + } + else { + setTimeout(function() { + inst._field = plugin._getSelection(inst, inst.elem[0], event); + postProcess(); + }, 0); + } + }, + + /** Extract the time value from a string as an array of values, or default to null. + @private + @param value {string} The date text. + @param inst {object} The instance settings. + @return {number[]} The retrieved time components (hours, minutes, seconds) or + null if no value. */ + _extractTime: function(inst, value) { + value = value || inst.elem.val(); + var currentTime = value.split(inst.options.separator); + if (inst.options.separator === '' && value !== '') { + currentTime[0] = value.substring(0, 2); + currentTime[1] = value.substring(2, 4); + currentTime[2] = value.substring(4, 6); + } + if (currentTime.length >= 2) { + var isAM = !inst.options.show24Hours && (value.indexOf(inst.options.ampmNames[0]) > -1); + var isPM = !inst.options.show24Hours && (value.indexOf(inst.options.ampmNames[1]) > -1); + var hour = parseInt(currentTime[0], 10); + hour = (isNaN(hour) ? 0 : hour); + hour = ((isAM || isPM) && hour === 12 ? 0 : hour) + (isPM ? 12 : 0); + var minute = parseInt(currentTime[1], 10); + minute = (isNaN(minute) ? 0 : minute); + var second = (currentTime.length >= 3 ? parseInt(currentTime[2], 10) : 0); + second = (isNaN(second) || !inst.options.showSeconds ? 0 : second); + return this._constrainTime(inst, [hour, minute, second]); + } + return null; + }, + + /** Constrain the given/current time to the time steps. + @private + @param inst {object} The instance settings. + @param fields {number[]} The current time components (hours, minutes, seconds). + @return {number[]} The constrained time components (hours, minutes, seconds). */ + _constrainTime: function(inst, fields) { + var specified = (fields !== null && fields !== undefined); + if (!specified) { + var now = this._determineTime(inst.options.defaultTime, inst) || new Date(); + fields = [now.getHours(), now.getMinutes(), now.getSeconds()]; + } + var reset = false; + for (var i = 0; i < inst.options.timeSteps.length; i++) { + if (reset) { + fields[i] = 0; + } + else if (inst.options.timeSteps[i] > 1) { + fields[i] = Math.round(fields[i] / inst.options.timeSteps[i]) * + inst.options.timeSteps[i]; + reset = true; + } + } + return fields; + }, + + /** Set the selected time into the input field. + @private + @param inst {object} The instance settings. */ + _showTime: function(inst) { + var currentTime = (inst.options.unlimitedHours ? inst._selectedHour : + this._formatNumber(inst.options.show24Hours ? inst._selectedHour : + ((inst._selectedHour + 11) % 12) + 1)) + inst.options.separator + + this._formatNumber(inst._selectedMinute) + + (inst.options.showSeconds ? inst.options.separator + + this._formatNumber(inst._selectedSecond) : '') + + (inst.options.show24Hours ? '' : inst.options.ampmPrefix + + inst.options.ampmNames[(inst._selectedHour < 12 ? 0 : 1)]); + this._setValue(inst, currentTime); + this._showField(inst); + }, + + /** Highlight the current date field. + @private + @param inst {object} The instance settings. */ + _showField: function(inst) { + var input = inst.elem[0]; + if (inst.elem.is(':hidden') || plugin._lastInput !== input) { + return; + } + var fieldSizes = [inst.elem.val().split(inst.options.separator)[0].length, 2, 2]; + var start = 0; + var field = 0; + while (field < inst._field) { + start += fieldSizes[field] + + (field === Math.max(1, inst._secondField) ? 0 : inst.options.separator.length); + field++; + } + var end = start + (inst._field !== inst._ampmField ? fieldSizes[field] : + inst.options.ampmPrefix.length + inst.options.ampmNames[0].length); + if (input.setSelectionRange) { // Mozilla + input.setSelectionRange(start, end); + } + else if (input.createTextRange) { // IE + var range = input.createTextRange(); + range.moveStart('character', start); + range.moveEnd('character', end - inst.elem.val().length); + range.select(); + } + if (!input.disabled) { + input.focus(); + } + }, + + /** Ensure displayed single number has a leading zero. + @private + @param value {number} The current value. + @return {string} Number with at least two digits. */ + _formatNumber: function(value) { + return (value < 10 ? '0' : '') + value; + }, + + /** Update the input field and notify listeners. + @private + @param inst {object} The instance settings. + @param value {string} The new value. */ + _setValue: function(inst, value) { + if (value !== inst.elem.val()) { + inst.elem.val(value).trigger('change'); + } + }, + + /** Move to previous/next field, or out of field altogether if appropriate. + @private + @param inst {object} The instance settings. + @param offset {number} The direction of change (-1, +1). + @param moveOut {boolean} true if can move out of the field. + @return {boolean} true if exiting the field, false if not. */ + _changeField: function(inst, offset, moveOut) { + var atFirstLast = (inst.elem.val() === '' || + inst._field === (offset === -1 ? 0 : Math.max(1, inst._secondField, inst._ampmField))); + if (!atFirstLast) { + inst._field += offset; + } + this._showField(inst); + inst._lastChr = ''; + return (atFirstLast && moveOut); + }, + + /** Update the current field in the direction indicated. + @private + @param inst {object} The instance settings. + @param offset {number} The amount to change by. */ + _adjustField: function(inst, offset) { + if (inst.elem.val() === '') { + offset = 0; + } + if (inst.options.unlimitedHours) { + this._setTime(inst, [inst._selectedHour + (inst._field === 0 ? offset * inst.options.timeSteps[0] : 0), + inst._selectedMinute + (inst._field === 1 ? offset * inst.options.timeSteps[1] : 0), + inst._selectedSecond + (inst._field === inst._secondField ? offset * inst.options.timeSteps[2] : 0)]); + } + else { + this._setTime(inst, new Date(0, 0, 0, + inst._selectedHour + (inst._field === 0 ? offset * inst.options.timeSteps[0] : 0) + + (inst._field === inst._ampmField ? offset * 12 : 0), + inst._selectedMinute + (inst._field === 1 ? offset * inst.options.timeSteps[1] : 0), + inst._selectedSecond + (inst._field === inst._secondField ? offset * inst.options.timeSteps[2] : 0))); + } + }, + + /** Check against minimum/maximum and display time. + @private + @param inst {object} The instance settings. + @param time {Date|number|string|number[]} The actual time or offset in seconds from now or + units and periods of offsets from now or numeric period values. */ + _setTime: function(inst, time) { + if (inst.options.unlimitedHours && $.isArray(time)) { + var fields = time; + } + else { + time = this._determineTime(time, inst); + var fields = (time ? [time.getHours(), time.getMinutes(), time.getSeconds()] : null); + } + fields = this._constrainTime(inst, fields); + time = new Date(0, 0, 0, fields[0], fields[1], fields[2]); + // Normalise to base date + var time = this._normaliseTime(time); + var minTime = this._normaliseTime(this._determineTime(inst.options.minTime, inst)); + var maxTime = this._normaliseTime(this._determineTime(inst.options.maxTime, inst)); + // Ensure it is within the bounds set + if (inst.options.unlimitedHours) { + while (fields[2] < 0) { + fields[2] += 60; + fields[1]--; + } + while (fields[2] > 59) { + fields[2] -= 60; + fields[1]++; + } + while (fields[1] < 0) { + fields[1] += 60; + fields[0]--; + } + while (fields[1] > 59) { + fields[1] -= 60; + fields[0]++; + } + minTime = (inst.options.minTime != null && $.isArray(inst.options.minTime)) ? + inst.options.minTime : [0, 0, 0]; + if (fields[0] < minTime[0]) { + fields = minTime.slice(0, 3); + } + else if (fields[0] === minTime[0]) { + if (fields[1] < minTime[1]) { + fields[1] = minTime[1]; + fields[2] = minTime[2]; + } + else if (fields[1] === minTime[1]) { + if (fields[2] < minTime[2]) { + fields[2] = minTime[2]; + } + } + } + if (inst.options.maxTime != null && $.isArray(inst.options.maxTime)) { + if (fields[0] > inst.options.maxTime[0]) { + fields = inst.options.maxTime.slice(0, 3); + } + else if (fields[0] === inst.options.maxTime[0]) { + if (fields[1] > inst.options.maxTime[1]) { + fields[1] = inst.options.maxTime[1]; + fields[2] = inst.options.maxTime[2]; + } + else if (fields[1] === inst.options.maxTime[1]) { + if (fields[2] > inst.options.maxTime[2]) { + fields[2] = inst.options.maxTime[2]; + } + } + } + } + } + else { + if (minTime && maxTime && minTime > maxTime) { + if (time < minTime && time > maxTime) { + time = (Math.abs(time - minTime) < Math.abs(time - maxTime) ? minTime : maxTime); + } + } + else { + time = (minTime && time < minTime ? minTime : + (maxTime && time > maxTime ? maxTime : time)); + } + fields[0] = time.getHours(); + fields[1] = time.getMinutes(); + fields[2] = time.getSeconds(); + } + // Perform further restrictions if required + if ($.isFunction(inst.options.beforeSetTime)) { + time = inst.options.beforeSetTime.apply(inst.elem[0], + [this.getTime(inst.elem[0]), time, minTime, maxTime]); + fields[0] = time.getHours(); + fields[1] = time.getMinutes(); + fields[2] = time.getSeconds(); + } + inst._selectedHour = fields[0]; + inst._selectedMinute = fields[1]; + inst._selectedSecond = fields[2]; + this._showTime(inst); + }, + + /** A time may be specified as an exact value or a relative one. + @private + @param setting {Date|number|string|number[]} The actual time or offset in seconds from now or + units and periods of offsets from now or numeric period values. + @param inst {object} The instance settings. + @return {Date} The calculated time. */ + _determineTime: function(setting, inst) { + var offsetNumeric = function(offset) { // E.g. +300, -2 + var time = new Date(); + time.setTime(time.getTime() + offset * 1000); + return time; + }; + var offsetString = function(offset) { // E.g. '+2m', '-4h', '+3h +30m' or '12:34:56PM' + var fields = plugin._extractTime(inst, offset); // Actual time? + var time = new Date(); + var hour = (fields ? fields[0] : time.getHours()); + var minute = (fields ? fields[1] : time.getMinutes()); + var second = (fields ? fields[2] : time.getSeconds()); + if (!fields) { + var pattern = /([+-]?[0-9]+)\s*(s|S|m|M|h|H)?/g; + var matches = pattern.exec(offset); + while (matches) { + switch (matches[2] || 's') { + case 's' : case 'S' : + second += parseInt(matches[1], 10); break; + case 'm' : case 'M' : + minute += parseInt(matches[1], 10); break; + case 'h' : case 'H' : + hour += parseInt(matches[1], 10); break; + } + matches = pattern.exec(offset); + } + } + time = new Date(0, 0, 10, hour, minute, second, 0); + if (/^!/.test(offset)) { // No wrapping + if (time.getDate() > 10) { + time = new Date(0, 0, 10, 23, 59, 59); + } + else if (time.getDate() < 10) { + time = new Date(0, 0, 10, 0, 0, 0); + } + } + return time; + }; + var offsetArray = function(setting) { + return new Date(0, 0, 0, setting[0], setting[1] || 0, setting[2] || 0, 0); + }; + return (setting ? (typeof setting === 'string' ? offsetString(setting) : + (typeof setting === 'number' ? offsetNumeric(setting) : + ($.isArray(setting) ? offsetArray(setting) : setting))) : null); + }, + + /** Normalise time object to a common date. + @private + @param time {Date} The original time. + @return {Date} The normalised time. */ + _normaliseTime: function(time) { + if (!time) { + return null; + } + time.setFullYear(1900); + time.setMonth(0); + time.setDate(0); + return time; + } + }); + + var plugin = $.timeEntry; + +})(jQuery);