Show More
Commit Description:
wip
Commit Description:
wip
References:
File last commit:
Show/Diff file:
Action:
vendor/javascript/datatables/DateTime-1.1.2/js/dataTables.dateTime.js
| 1564 lines
| 37.7 KiB
| application/javascript
| JavascriptLexer
|
r866 | /*! DateTime picker for DataTables.net v1.1.2 | |||
* | ||||
* © SpryMedia Ltd, all rights reserved. | ||||
* License: MIT datatables.net/license/mit | ||||
*/ | ||||
/** | ||||
* @summary DateTime picker for DataTables.net | ||||
* @version 1.1.2 | ||||
* @file dataTables.dateTime.js | ||||
* @author SpryMedia Ltd | ||||
* @contact www.datatables.net/contact | ||||
*/ | ||||
(function( factory ){ | ||||
if ( typeof define === 'function' && define.amd ) { | ||||
// AMD | ||||
define( ['jquery'], function ( $ ) { | ||||
return factory( $, window, document ); | ||||
} ); | ||||
} | ||||
else if ( typeof exports === 'object' ) { | ||||
// CommonJS | ||||
module.exports = function (root, $) { | ||||
if ( ! root ) { | ||||
root = window; | ||||
} | ||||
return factory( $, root, root.document ); | ||||
}; | ||||
} | ||||
else { | ||||
// Browser | ||||
factory( jQuery, window, document ); | ||||
} | ||||
}(function( $, window, document, undefined ) { | ||||
'use strict'; | ||||
// Supported formatting and parsing libraries: | ||||
// * Moment | ||||
// * Luxon | ||||
// * DayJS | ||||
var dateLib; | ||||
/* | ||||
* This file provides a DateTime GUI picker (calendar and time input). Only the | ||||
* format YYYY-MM-DD is supported without additional software, but the end user | ||||
* experience can be greatly enhanced by including the momentjs, dayjs or luxon library | ||||
* which provide date / time parsing and formatting options. | ||||
* | ||||
* This functionality is required because the HTML5 date and datetime input | ||||
* types are not widely supported in desktop browsers. | ||||
* | ||||
* Constructed by using: | ||||
* | ||||
* new DateTime( input, opts ) | ||||
* | ||||
* where `input` is the HTML input element to use and `opts` is an object of | ||||
* options based on the `DateTime.defaults` object. | ||||
*/ | ||||
var DateTime = function ( input, opts ) { | ||||
// Attempt to auto detect the formatting library (if there is one). Having it in | ||||
// the constructor allows load order independence. | ||||
if (typeof dateLib === 'undefined') { | ||||
dateLib = window.moment | ||||
? window.moment | ||||
: window.dayjs | ||||
? window.dayjs | ||||
: window.luxon | ||||
? window.luxon | ||||
: null; | ||||
} | ||||
this.c = $.extend( true, {}, DateTime.defaults, opts ); | ||||
var classPrefix = this.c.classPrefix; | ||||
var i18n = this.c.i18n; | ||||
// Only IS8601 dates are supported without moment, dayjs or luxon | ||||
if ( ! dateLib && this.c.format !== 'YYYY-MM-DD' ) { | ||||
throw "DateTime: Without momentjs, dayjs or luxon only the format 'YYYY-MM-DD' can be used"; | ||||
} | ||||
// Min and max need to be `Date` objects in the config | ||||
if (typeof this.c.minDate === 'string') { | ||||
this.c.minDate = new Date(this.c.minDate); | ||||
} | ||||
if (typeof this.c.maxDate === 'string') { | ||||
this.c.maxDate = new Date(this.c.maxDate); | ||||
} | ||||
var timeBlock = function ( type ) { | ||||
return '<div class="'+classPrefix+'-timeblock">'+ | ||||
'</div>'; | ||||
}; | ||||
var gap = function () { | ||||
return '<span>:</span>'; | ||||
}; | ||||
// DOM structure | ||||
var structure = $( | ||||
'<div class="'+classPrefix+'">'+ | ||||
'<div class="'+classPrefix+'-date">'+ | ||||
'<div class="'+classPrefix+'-title">'+ | ||||
'<div class="'+classPrefix+'-iconLeft">'+ | ||||
'<button type="button" title="'+i18n.previous+'">'+i18n.previous+'</button>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-iconRight">'+ | ||||
'<button type="button" title="'+i18n.next+'">'+i18n.next+'</button>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-label">'+ | ||||
'<span></span>'+ | ||||
'<select class="'+classPrefix+'-month"></select>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-label">'+ | ||||
'<span></span>'+ | ||||
'<select class="'+classPrefix+'-year"></select>'+ | ||||
'</div>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-buttons">'+ | ||||
'<a class="'+classPrefix+'-clear">'+i18n.clear+'</a>'+ | ||||
'<a class="'+classPrefix+'-today">'+i18n.today+'</a>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-calendar"></div>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-time">'+ | ||||
'<div class="'+classPrefix+'-hours"></div>'+ | ||||
'<div class="'+classPrefix+'-minutes"></div>'+ | ||||
'<div class="'+classPrefix+'-seconds"></div>'+ | ||||
'</div>'+ | ||||
'<div class="'+classPrefix+'-error"></div>'+ | ||||
'</div>' | ||||
); | ||||
this.dom = { | ||||
container: structure, | ||||
date: structure.find( '.'+classPrefix+'-date' ), | ||||
title: structure.find( '.'+classPrefix+'-title' ), | ||||
calendar: structure.find( '.'+classPrefix+'-calendar' ), | ||||
time: structure.find( '.'+classPrefix+'-time' ), | ||||
error: structure.find( '.'+classPrefix+'-error' ), | ||||
buttons: structure.find( '.'+classPrefix+'-buttons' ), | ||||
clear: structure.find( '.'+classPrefix+'-clear' ), | ||||
today: structure.find( '.'+classPrefix+'-today' ), | ||||
input: $(input) | ||||
}; | ||||
this.s = { | ||||
/** @type {Date} Date value that the picker has currently selected */ | ||||
d: null, | ||||
/** @type {Date} Date of the calendar - might not match the value */ | ||||
display: null, | ||||
/** @type {number} Used to select minutes in a range where the range base is itself unavailable */ | ||||
minutesRange: null, | ||||
/** @type {number} Used to select minutes in a range where the range base is itself unavailable */ | ||||
secondsRange: null, | ||||
/** @type {String} Unique namespace string for this instance */ | ||||
namespace: 'dateime-'+(DateTime._instance++), | ||||
/** @type {Object} Parts of the picker that should be shown */ | ||||
parts: { | ||||
date: this.c.format.match( /[YMD]|L(?!T)|l/ ) !== null, | ||||
time: this.c.format.match( /[Hhm]|LT|LTS/ ) !== null, | ||||
seconds: this.c.format.indexOf( 's' ) !== -1, | ||||
hours12: this.c.format.match( /[haA]/ ) !== null | ||||
} | ||||
}; | ||||
this.dom.container | ||||
.append( this.dom.date ) | ||||
.append( this.dom.time ) | ||||
.append( this.dom.error ); | ||||
this.dom.date | ||||
.append( this.dom.title ) | ||||
.append( this.dom.buttons ) | ||||
.append( this.dom.calendar ); | ||||
this._constructor(); | ||||
}; | ||||
$.extend( DateTime.prototype, { | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* Public | ||||
*/ | ||||
/** | ||||
* Destroy the control | ||||
*/ | ||||
destroy: function () { | ||||
this._hide(true); | ||||
this.dom.container.off().empty(); | ||||
this.dom.input | ||||
.removeAttr('autocomplete') | ||||
.off('.datetime'); | ||||
}, | ||||
errorMsg: function ( msg ) { | ||||
var error = this.dom.error; | ||||
if ( msg ) { | ||||
error.html( msg ); | ||||
} | ||||
else { | ||||
error.empty(); | ||||
} | ||||
return this; | ||||
}, | ||||
hide: function () { | ||||
this._hide(); | ||||
return this; | ||||
}, | ||||
max: function ( date ) { | ||||
this.c.maxDate = typeof date === 'string' | ||||
? new Date(date) | ||||
: date; | ||||
this._optionsTitle(); | ||||
this._setCalander(); | ||||
return this; | ||||
}, | ||||
min: function ( date ) { | ||||
this.c.minDate = typeof date === 'string' | ||||
? new Date(date) | ||||
: date; | ||||
this._optionsTitle(); | ||||
this._setCalander(); | ||||
return this; | ||||
}, | ||||
/** | ||||
* Check if an element belongs to this control | ||||
* | ||||
* @param {node} node Element to check | ||||
* @return {boolean} true if owned by this control, false otherwise | ||||
*/ | ||||
owns: function ( node ) { | ||||
return $(node).parents().filter( this.dom.container ).length > 0; | ||||
}, | ||||
/** | ||||
* Get / set the value | ||||
* | ||||
* @param {string|Date} set Value to set | ||||
* @param {boolean} [write=true] Flag to indicate if the formatted value | ||||
* should be written into the input element | ||||
*/ | ||||
val: function ( set, write ) { | ||||
if ( set === undefined ) { | ||||
return this.s.d; | ||||
} | ||||
if ( set instanceof Date ) { | ||||
this.s.d = this._dateToUtc( set ); | ||||
} | ||||
else if ( set === null || set === '' ) { | ||||
this.s.d = null; | ||||
} | ||||
else if ( set === '--now' ) { | ||||
this.s.d = new Date(); | ||||
} | ||||
else if ( typeof set === 'string' ) { | ||||
// luxon uses different method names so need to be able to call them | ||||
if(dateLib && dateLib == window.luxon) { | ||||
var luxDT = dateLib.DateTime.fromFormat(set, this.c.format) | ||||
this.s.d = luxDT.isValid ? luxDT.toJSDate() : null; | ||||
} | ||||
else if ( dateLib ) { | ||||
// Use moment, dayjs or luxon if possible (even for ISO8601 strings, since it | ||||
// will correctly handle 0000-00-00 and the like) | ||||
var m = dateLib.utc( set, this.c.format, this.c.locale, this.c.strict ); | ||||
this.s.d = m.isValid() ? m.toDate() : null; | ||||
} | ||||
else { | ||||
// Else must be using ISO8601 without a date library (constructor would | ||||
// have thrown an error otherwise) | ||||
var match = set.match(/(\d{4})\-(\d{2})\-(\d{2})/ ); | ||||
this.s.d = match ? | ||||
new Date( Date.UTC(match[1], match[2]-1, match[3]) ) : | ||||
null; | ||||
} | ||||
} | ||||
if ( write || write === undefined ) { | ||||
if ( this.s.d ) { | ||||
this._writeOutput(); | ||||
} | ||||
else { | ||||
// The input value was not valid... | ||||
this.dom.input.val( set ); | ||||
} | ||||
} | ||||
// Need something to display | ||||
this.s.display = this.s.d | ||||
? new Date( this.s.d.toString() ) | ||||
: new Date(); | ||||
// Set the day of the month to be 1 so changing between months doesn't | ||||
// run into issues when going from day 31 to 28 (for example) | ||||
this.s.display.setUTCDate( 1 ); | ||||
// Update the display elements for the new value | ||||
this._setTitle(); | ||||
this._setCalander(); | ||||
this._setTime(); | ||||
return this; | ||||
}, | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* Constructor | ||||
*/ | ||||
/** | ||||
* Build the control and assign initial event handlers | ||||
* | ||||
* @private | ||||
*/ | ||||
_constructor: function () { | ||||
var that = this; | ||||
var classPrefix = this.c.classPrefix; | ||||
var last = this.dom.input.val(); | ||||
var onChange = function () { | ||||
var curr = that.dom.input.val(); | ||||
if (curr !== last) { | ||||
that.c.onChange.call( that, curr, that.s.d, that.dom.input ); | ||||
last = curr; | ||||
} | ||||
}; | ||||
if ( ! this.s.parts.date ) { | ||||
this.dom.date.css( 'display', 'none' ); | ||||
} | ||||
if ( ! this.s.parts.time ) { | ||||
this.dom.time.css( 'display', 'none' ); | ||||
} | ||||
if ( ! this.s.parts.seconds ) { | ||||
this.dom.time.children('div.'+classPrefix+'-seconds').remove(); | ||||
this.dom.time.children('span').eq(1).remove(); | ||||
} | ||||
if ( ! this.c.buttons.clear ) { | ||||
this.dom.clear.css( 'display', 'none' ); | ||||
} | ||||
if ( ! this.c.buttons.today ) { | ||||
this.dom.today.css( 'display', 'none' ); | ||||
} | ||||
// Render the options | ||||
this._optionsTitle(); | ||||
$(document).on('i18n.dt', function (e, settings) { | ||||
if (settings.oLanguage.datetime) { | ||||
$.extend(true, that.c.i18n, settings.oLanguage.datetime); | ||||
that._optionsTitle(); | ||||
} | ||||
}); | ||||
// When attached to a hidden input, we always show the input picker, and | ||||
// do so inline | ||||
if (this.dom.input.attr('type') === 'hidden') { | ||||
this.dom.container.addClass('inline'); | ||||
this.c.attachTo = 'input'; | ||||
this.val( this.dom.input.val(), false ); | ||||
this._show(); | ||||
} | ||||
// Set the initial value | ||||
if (last) { | ||||
this.val( last, false ); | ||||
} | ||||
// Trigger the display of the widget when clicking or focusing on the | ||||
// input element | ||||
this.dom.input | ||||
.attr('autocomplete', 'off') | ||||
.on('focus.datetime click.datetime', function () { | ||||
// If already visible - don't do anything | ||||
if ( that.dom.container.is(':visible') || that.dom.input.is(':disabled') ) { | ||||
return; | ||||
} | ||||
// In case the value has changed by text | ||||
that.val( that.dom.input.val(), false ); | ||||
that._show(); | ||||
} ) | ||||
.on('keyup.datetime', function () { | ||||
// Update the calendar's displayed value as the user types | ||||
if ( that.dom.container.is(':visible') ) { | ||||
that.val( that.dom.input.val(), false ); | ||||
} | ||||
} ); | ||||
// Main event handlers for input in the widget | ||||
this.dom.container | ||||
.on( 'change', 'select', function () { | ||||
var select = $(this); | ||||
var val = select.val(); | ||||
if ( select.hasClass(classPrefix+'-month') ) { | ||||
// Month select | ||||
that._correctMonth( that.s.display, val ); | ||||
that._setTitle(); | ||||
that._setCalander(); | ||||
} | ||||
else if ( select.hasClass(classPrefix+'-year') ) { | ||||
// Year select | ||||
that.s.display.setUTCFullYear( val ); | ||||
that._setTitle(); | ||||
that._setCalander(); | ||||
} | ||||
else if ( select.hasClass(classPrefix+'-hours') || select.hasClass(classPrefix+'-ampm') ) { | ||||
// Hours - need to take account of AM/PM input if present | ||||
if ( that.s.parts.hours12 ) { | ||||
var hours = $(that.dom.container).find('.'+classPrefix+'-hours').val() * 1; | ||||
var pm = $(that.dom.container).find('.'+classPrefix+'-ampm').val() === 'pm'; | ||||
that.s.d.setUTCHours( hours === 12 && !pm ? | ||||
0 : | ||||
pm && hours !== 12 ? | ||||
hours + 12 : | ||||
hours | ||||
); | ||||
} | ||||
else { | ||||
that.s.d.setUTCHours( val ); | ||||
} | ||||
that._setTime(); | ||||
that._writeOutput( true ); | ||||
onChange(); | ||||
} | ||||
else if ( select.hasClass(classPrefix+'-minutes') ) { | ||||
// Minutes select | ||||
that.s.d.setUTCMinutes( val ); | ||||
that._setTime(); | ||||
that._writeOutput( true ); | ||||
onChange(); | ||||
} | ||||
else if ( select.hasClass(classPrefix+'-seconds') ) { | ||||
// Seconds select | ||||
that.s.d.setSeconds( val ); | ||||
that._setTime(); | ||||
that._writeOutput( true ); | ||||
onChange(); | ||||
} | ||||
that.dom.input.focus(); | ||||
that._position(); | ||||
} ) | ||||
.on( 'click', function (e) { | ||||
var d = that.s.d; | ||||
var nodeName = e.target.nodeName.toLowerCase(); | ||||
var target = nodeName === 'span' ? | ||||
e.target.parentNode : | ||||
e.target; | ||||
nodeName = target.nodeName.toLowerCase(); | ||||
if ( nodeName === 'select' ) { | ||||
return; | ||||
} | ||||
e.stopPropagation(); | ||||
if ( nodeName === 'a' ) { | ||||
e.preventDefault(); | ||||
if ($(target).hasClass(classPrefix+'-clear')) { | ||||
// Clear the value and don't change the display | ||||
that.s.d = null; | ||||
that.dom.input.val(''); | ||||
that._writeOutput(); | ||||
that._setCalander(); | ||||
that._setTime(); | ||||
onChange(); | ||||
} | ||||
else if ($(target).hasClass(classPrefix+'-today')) { | ||||
// Don't change the value, but jump to the month | ||||
// containing today | ||||
that.s.display = new Date(); | ||||
that._setTitle(); | ||||
that._setCalander(); | ||||
} | ||||
} | ||||
if ( nodeName === 'button' ) { | ||||
var button = $(target); | ||||
var parent = button.parent(); | ||||
if ( parent.hasClass('disabled') && ! parent.hasClass('range') ) { | ||||
button.blur(); | ||||
return; | ||||
} | ||||
if ( parent.hasClass(classPrefix+'-iconLeft') ) { | ||||
// Previous month | ||||
that.s.display.setUTCMonth( that.s.display.getUTCMonth()-1 ); | ||||
that._setTitle(); | ||||
that._setCalander(); | ||||
that.dom.input.focus(); | ||||
} | ||||
else if ( parent.hasClass(classPrefix+'-iconRight') ) { | ||||
// Next month | ||||
that._correctMonth( that.s.display, that.s.display.getUTCMonth()+1 ); | ||||
that._setTitle(); | ||||
that._setCalander(); | ||||
that.dom.input.focus(); | ||||
} | ||||
else if ( button.parents('.'+classPrefix+'-time').length ) { | ||||
var val = button.data('value'); | ||||
var unit = button.data('unit'); | ||||
d = that._needValue(); | ||||
if ( unit === 'minutes' ) { | ||||
if ( parent.hasClass('disabled') && parent.hasClass('range') ) { | ||||
that.s.minutesRange = val; | ||||
that._setTime(); | ||||
return; | ||||
} | ||||
else { | ||||
that.s.minutesRange = null; | ||||
} | ||||
} | ||||
if ( unit === 'seconds' ) { | ||||
if ( parent.hasClass('disabled') && parent.hasClass('range') ) { | ||||
that.s.secondsRange = val; | ||||
that._setTime(); | ||||
return; | ||||
} | ||||
else { | ||||
that.s.secondsRange = null; | ||||
} | ||||
} | ||||
// Specific to hours for 12h clock | ||||
if ( val === 'am' ) { | ||||
if ( d.getUTCHours() >= 12 ) { | ||||
val = d.getUTCHours() - 12; | ||||
} | ||||
else { | ||||
return; | ||||
} | ||||
} | ||||
else if ( val === 'pm' ) { | ||||
if ( d.getUTCHours() < 12 ) { | ||||
val = d.getUTCHours() + 12; | ||||
} | ||||
else { | ||||
return; | ||||
} | ||||
} | ||||
var set = unit === 'hours' ? | ||||
'setUTCHours' : | ||||
unit === 'minutes' ? | ||||
'setUTCMinutes' : | ||||
'setSeconds'; | ||||
d[set]( val ); | ||||
that._setTime(); | ||||
that._writeOutput( true ); | ||||
onChange(); | ||||
} | ||||
else { | ||||
// Calendar click | ||||
d = that._needValue(); | ||||
// Can't be certain that the current day will exist in | ||||
// the new month, and likewise don't know that the | ||||
// new day will exist in the old month, But 1 always | ||||
// does, so we can change the month without worry of a | ||||
// recalculation being done automatically by `Date` | ||||
d.setUTCDate( 1 ); | ||||
d.setUTCFullYear( button.data('year') ); | ||||
d.setUTCMonth( button.data('month') ); | ||||
d.setUTCDate( button.data('day') ); | ||||
that._writeOutput( true ); | ||||
// Don't hide if there is a time picker, since we want to | ||||
// be able to select a time as well. | ||||
if ( ! that.s.parts.time ) { | ||||
// This is annoying but IE has some kind of async | ||||
// behaviour with focus and the focus from the above | ||||
// write would occur after this hide - resulting in the | ||||
// calendar opening immediately | ||||
setTimeout( function () { | ||||
that._hide(); | ||||
}, 10 ); | ||||
} | ||||
else { | ||||
that._setCalander(); | ||||
} | ||||
onChange(); | ||||
} | ||||
} | ||||
else { | ||||
// Click anywhere else in the widget - return focus to the | ||||
// input element | ||||
that.dom.input.focus(); | ||||
} | ||||
} ); | ||||
}, | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* Private | ||||
*/ | ||||
/** | ||||
* Compare the date part only of two dates - this is made super easy by the | ||||
* toDateString method! | ||||
* | ||||
* @param {Date} a Date 1 | ||||
* @param {Date} b Date 2 | ||||
* @private | ||||
*/ | ||||
_compareDates: function( a, b ) { | ||||
// Can't use toDateString as that converts to local time | ||||
// luxon uses different method names so need to be able to call them | ||||
return dateLib && dateLib == window.luxon | ||||
? dateLib.DateTime.fromJSDate(a).toISODate() === dateLib.DateTime.fromJSDate(b).toISODate() | ||||
: this._dateToUtcString(a) === this._dateToUtcString(b); | ||||
}, | ||||
/** | ||||
* When changing month, take account of the fact that some months don't have | ||||
* the same number of days. For example going from January to February you | ||||
* can have the 31st of Jan selected and just add a month since the date | ||||
* would still be 31, and thus drop you into March. | ||||
* | ||||
* @param {Date} date Date - will be modified | ||||
* @param {integer} month Month to set | ||||
* @private | ||||
*/ | ||||
_correctMonth: function ( date, month ) { | ||||
var days = this._daysInMonth( date.getUTCFullYear(), month ); | ||||
var correctDays = date.getUTCDate() > days; | ||||
date.setUTCMonth( month ); | ||||
if ( correctDays ) { | ||||
date.setUTCDate( days ); | ||||
date.setUTCMonth( month ); | ||||
} | ||||
}, | ||||
/** | ||||
* Get the number of days in a method. Based on | ||||
* http://stackoverflow.com/a/4881951 by Matti Virkkunen | ||||
* | ||||
* @param {integer} year Year | ||||
* @param {integer} month Month (starting at 0) | ||||
* @private | ||||
*/ | ||||
_daysInMonth: function ( year, month ) { | ||||
// | ||||
var isLeap = ((year % 4) === 0 && ((year % 100) !== 0 || (year % 400) === 0)); | ||||
var months = [31, (isLeap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; | ||||
return months[month]; | ||||
}, | ||||
/** | ||||
* Create a new date object which has the UTC values set to the local time. | ||||
* This allows the local time to be used directly for the library which | ||||
* always bases its calculations and display on UTC. | ||||
* | ||||
* @param {Date} s Date to "convert" | ||||
* @return {Date} Shifted date | ||||
*/ | ||||
_dateToUtc: function ( s ) { | ||||
return new Date( Date.UTC( | ||||
s.getFullYear(), s.getMonth(), s.getDate(), | ||||
s.getHours(), s.getMinutes(), s.getSeconds() | ||||
) ); | ||||
}, | ||||
/** | ||||
* Create a UTC ISO8601 date part from a date object | ||||
* | ||||
* @param {Date} d Date to "convert" | ||||
* @return {string} ISO formatted date | ||||
*/ | ||||
_dateToUtcString: function ( d ) { | ||||
// luxon uses different method names so need to be able to call them | ||||
return dateLib && dateLib == window.luxon | ||||
? dateLib.DateTime.fromJSDate(d).toISODate() | ||||
: d.getUTCFullYear()+'-'+ | ||||
this._pad(d.getUTCMonth()+1)+'-'+ | ||||
this._pad(d.getUTCDate()); | ||||
}, | ||||
/** | ||||
* Hide the control and remove events related to its display | ||||
* | ||||
* @private | ||||
*/ | ||||
_hide: function (destroy) { | ||||
if (! destroy && this.dom.input.attr('type') === 'hidden') { | ||||
return; | ||||
} | ||||
var namespace = this.s.namespace; | ||||
this.dom.container.detach(); | ||||
$(window).off( '.'+namespace ); | ||||
$(document).off( 'keydown.'+namespace ); | ||||
$('div.dataTables_scrollBody').off( 'scroll.'+namespace ); | ||||
$('div.DTE_Body_Content').off( 'scroll.'+namespace ); | ||||
$('body').off( 'click.'+namespace ); | ||||
$(this.dom.input[0].offsetParent).off('.'+namespace); | ||||
}, | ||||
/** | ||||
* Convert a 24 hour value to a 12 hour value | ||||
* | ||||
* @param {integer} val 24 hour value | ||||
* @return {integer} 12 hour value | ||||
* @private | ||||
*/ | ||||
_hours24To12: function ( val ) { | ||||
return val === 0 ? | ||||
12 : | ||||
val > 12 ? | ||||
val - 12 : | ||||
val; | ||||
}, | ||||
/** | ||||
* Generate the HTML for a single day in the calendar - this is basically | ||||
* and HTML cell with a button that has data attributes so we know what was | ||||
* clicked on (if it is clicked on) and a bunch of classes for styling. | ||||
* | ||||
* @param {object} day Day object from the `_htmlMonth` method | ||||
* @return {string} HTML cell | ||||
*/ | ||||
_htmlDay: function( day ) | ||||
{ | ||||
if ( day.empty ) { | ||||
return '<td class="empty"></td>'; | ||||
} | ||||
var classes = [ 'selectable' ]; | ||||
var classPrefix = this.c.classPrefix; | ||||
if ( day.disabled ) { | ||||
classes.push( 'disabled' ); | ||||
} | ||||
if ( day.today ) { | ||||
classes.push( 'now' ); | ||||
} | ||||
if ( day.selected ) { | ||||
classes.push( 'selected' ); | ||||
} | ||||
return '<td data-day="' + day.day + '" class="' + classes.join(' ') + '">' + | ||||
'<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" ' +'data-year="' + day.year + '" data-month="' + day.month + '" data-day="' + day.day + '">' + | ||||
'<span>'+day.day+'</span>'+ | ||||
'</button>' + | ||||
'</td>'; | ||||
}, | ||||
/** | ||||
* Create the HTML for a month to be displayed in the calendar table. | ||||
* | ||||
* Based upon the logic used in Pikaday - MIT licensed | ||||
* Copyright (c) 2014 David Bushell | ||||
* https://github.com/dbushell/Pikaday | ||||
* | ||||
* @param {integer} year Year | ||||
* @param {integer} month Month (starting at 0) | ||||
* @return {string} Calendar month HTML | ||||
* @private | ||||
*/ | ||||
_htmlMonth: function ( year, month ) { | ||||
var now = this._dateToUtc( new Date() ), | ||||
days = this._daysInMonth( year, month ), | ||||
before = new Date( Date.UTC(year, month, 1) ).getUTCDay(), | ||||
data = [], | ||||
row = []; | ||||
if ( this.c.firstDay > 0 ) { | ||||
before -= this.c.firstDay; | ||||
if (before < 0) { | ||||
before += 7; | ||||
} | ||||
} | ||||
var cells = days + before, | ||||
after = cells; | ||||
while ( after > 7 ) { | ||||
after -= 7; | ||||
} | ||||
cells += 7 - after; | ||||
var minDate = this.c.minDate; | ||||
var maxDate = this.c.maxDate; | ||||
if ( minDate ) { | ||||
minDate.setUTCHours(0); | ||||
minDate.setUTCMinutes(0); | ||||
minDate.setSeconds(0); | ||||
} | ||||
if ( maxDate ) { | ||||
maxDate.setUTCHours(23); | ||||
maxDate.setUTCMinutes(59); | ||||
maxDate.setSeconds(59); | ||||
} | ||||
for ( var i=0, r=0 ; i<cells ; i++ ) { | ||||
var day = new Date( Date.UTC(year, month, 1 + (i - before)) ), | ||||
selected = this.s.d ? this._compareDates(day, this.s.d) : false, | ||||
today = this._compareDates(day, now), | ||||
empty = i < before || i >= (days + before), | ||||
disabled = (minDate && day < minDate) || | ||||
(maxDate && day > maxDate); | ||||
var disableDays = this.c.disableDays; | ||||
if ( Array.isArray( disableDays ) && $.inArray( day.getUTCDay(), disableDays ) !== -1 ) { | ||||
disabled = true; | ||||
} | ||||
else if ( typeof disableDays === 'function' && disableDays( day ) === true ) { | ||||
disabled = true; | ||||
} | ||||
var dayConfig = { | ||||
day: 1 + (i - before), | ||||
month: month, | ||||
year: year, | ||||
selected: selected, | ||||
today: today, | ||||
disabled: disabled, | ||||
empty: empty | ||||
}; | ||||
row.push( this._htmlDay(dayConfig) ); | ||||
if ( ++r === 7 ) { | ||||
if ( this.c.showWeekNumber ) { | ||||
row.unshift( this._htmlWeekOfYear(i - before, month, year) ); | ||||
} | ||||
data.push( '<tr>'+row.join('')+'</tr>' ); | ||||
row = []; | ||||
r = 0; | ||||
} | ||||
} | ||||
var classPrefix = this.c.classPrefix; | ||||
var className = classPrefix+'-table'; | ||||
if ( this.c.showWeekNumber ) { | ||||
className += ' weekNumber'; | ||||
} | ||||
// Show / hide month icons based on min/max | ||||
if ( minDate ) { | ||||
var underMin = minDate >= new Date( Date.UTC(year, month, 1, 0, 0, 0 ) ); | ||||
this.dom.title.find('div.'+classPrefix+'-iconLeft') | ||||
.css( 'display', underMin ? 'none' : 'block' ); | ||||
} | ||||
if ( maxDate ) { | ||||
var overMax = maxDate < new Date( Date.UTC(year, month+1, 1, 0, 0, 0 ) ); | ||||
this.dom.title.find('div.'+classPrefix+'-iconRight') | ||||
.css( 'display', overMax ? 'none' : 'block' ); | ||||
} | ||||
return '<table class="'+className+'">' + | ||||
'<thead>'+ | ||||
this._htmlMonthHead() + | ||||
'</thead>'+ | ||||
'<tbody>'+ | ||||
data.join('') + | ||||
'</tbody>'+ | ||||
'</table>'; | ||||
}, | ||||
/** | ||||
* Create the calendar table's header (week days) | ||||
* | ||||
* @return {string} HTML cells for the row | ||||
* @private | ||||
*/ | ||||
_htmlMonthHead: function () { | ||||
var a = []; | ||||
var firstDay = this.c.firstDay; | ||||
var i18n = this.c.i18n; | ||||
// Take account of the first day shift | ||||
var dayName = function ( day ) { | ||||
day += firstDay; | ||||
while (day >= 7) { | ||||
day -= 7; | ||||
} | ||||
return i18n.weekdays[day]; | ||||
}; | ||||
// Empty cell in the header | ||||
if ( this.c.showWeekNumber ) { | ||||
a.push( '<th></th>' ); | ||||
} | ||||
for ( var i=0 ; i<7 ; i++ ) { | ||||
a.push( '<th>'+dayName( i )+'</th>' ); | ||||
} | ||||
return a.join(''); | ||||
}, | ||||
/** | ||||
* Create a cell that contains week of the year - ISO8601 | ||||
* | ||||
* Based on https://stackoverflow.com/questions/6117814/ and | ||||
* http://techblog.procurios.nl/k/n618/news/view/33796/14863/ | ||||
* | ||||
* @param {integer} d Day of month | ||||
* @param {integer} m Month of year (zero index) | ||||
* @param {integer} y Year | ||||
* @return {string} | ||||
* @private | ||||
*/ | ||||
_htmlWeekOfYear: function ( d, m, y ) { | ||||
var date = new Date( y, m, d, 0, 0, 0, 0 ); | ||||
// First week of the year always has 4th January in it | ||||
date.setDate( date.getDate() + 4 - (date.getDay() || 7) ); | ||||
var oneJan = new Date( y, 0, 1 ); | ||||
var weekNum = Math.ceil( ( ( (date - oneJan) / 86400000) + 1)/7 ); | ||||
return '<td class="'+this.c.classPrefix+'-week">' + weekNum + '</td>'; | ||||
}, | ||||
/** | ||||
* Check if the instance has a date object value - it might be null. | ||||
* If is doesn't set one to now. | ||||
* @returns A Date object | ||||
* @private | ||||
*/ | ||||
_needValue: function () { | ||||
if ( ! this.s.d ) { | ||||
this.s.d = this._dateToUtc( new Date() ); | ||||
} | ||||
return this.s.d; | ||||
}, | ||||
/** | ||||
* Create option elements from a range in an array | ||||
* | ||||
* @param {string} selector Class name unique to the select element to use | ||||
* @param {array} values Array of values | ||||
* @param {array} [labels] Array of labels. If given must be the same | ||||
* length as the values parameter. | ||||
* @private | ||||
*/ | ||||
_options: function ( selector, values, labels ) { | ||||
if ( ! labels ) { | ||||
labels = values; | ||||
} | ||||
var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector); | ||||
select.empty(); | ||||
for ( var i=0, ien=values.length ; i<ien ; i++ ) { | ||||
select.append( '<option value="'+values[i]+'">'+labels[i]+'</option>' ); | ||||
} | ||||
}, | ||||
/** | ||||
* Set an option and update the option's span pair (since the select element | ||||
* has opacity 0 for styling) | ||||
* | ||||
* @param {string} selector Class name unique to the select element to use | ||||
* @param {*} val Value to set | ||||
* @private | ||||
*/ | ||||
_optionSet: function ( selector, val ) { | ||||
var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector); | ||||
var span = select.parent().children('span'); | ||||
select.val( val ); | ||||
var selected = select.find('option:selected'); | ||||
span.html( selected.length !== 0 ? | ||||
selected.text() : | ||||
this.c.i18n.unknown | ||||
); | ||||
}, | ||||
/** | ||||
* Create time options list. | ||||
* | ||||
* @param {string} unit Time unit - hours, minutes or seconds | ||||
* @param {integer} count Count range - 12, 24 or 60 | ||||
* @param {integer} val Existing value for this unit | ||||
* @param {integer[]} allowed Values allow for selection | ||||
* @param {integer} range Override range | ||||
* @private | ||||
*/ | ||||
_optionsTime: function ( unit, count, val, allowed, range ) { | ||||
var classPrefix = this.c.classPrefix; | ||||
var container = this.dom.container.find('div.'+classPrefix+'-'+unit); | ||||
var i, j; | ||||
var render = count === 12 ? | ||||
function (i) { return i; } : | ||||
this._pad; | ||||
var classPrefix = this.c.classPrefix; | ||||
var className = classPrefix+'-table'; | ||||
var i18n = this.c.i18n; | ||||
if ( ! container.length ) { | ||||
return; | ||||
} | ||||
var a = ''; | ||||
var span = 10; | ||||
var button = function (value, label, className) { | ||||
// Shift the value for PM | ||||
if ( count === 12 && typeof value === 'number' ) { | ||||
if (val >= 12 ) { | ||||
value += 12; | ||||
} | ||||
if (value == 12) { | ||||
value = 0; | ||||
} | ||||
else if (value == 24) { | ||||
value = 12; | ||||
} | ||||
} | ||||
var selected = val === value || (value === 'am' && val < 12) || (value === 'pm' && val >= 12) ? | ||||
'selected' : | ||||
''; | ||||
if (allowed && $.inArray(value, allowed) === -1) { | ||||
selected += ' disabled'; | ||||
} | ||||
if ( className ) { | ||||
selected += ' '+className; | ||||
} | ||||
return '<td class="selectable '+selected+'">' + | ||||
'<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" data-unit="'+unit+'" data-value="'+value+ '">' + | ||||
'<span>'+label+'</span>'+ | ||||
'</button>' + | ||||
'</td>'; | ||||
} | ||||
if ( count === 12 ) { | ||||
// Hours with AM/PM | ||||
a += '<tr>'; | ||||
for ( i=1 ; i<=6 ; i++ ) { | ||||
a += button(i, render(i)); | ||||
} | ||||
a += button('am', i18n.amPm[0]); | ||||
a += '</tr>'; | ||||
a += '<tr>'; | ||||
for ( i=7 ; i<=12 ; i++ ) { | ||||
a += button(i, render(i)); | ||||
} | ||||
a += button('pm', i18n.amPm[1]); | ||||
a += '</tr>'; | ||||
span = 7; | ||||
} | ||||
else if ( count === 24 ) { | ||||
// Hours - 24 | ||||
var c = 0; | ||||
for (j=0 ; j<4 ; j++ ) { | ||||
a += '<tr>'; | ||||
for ( i=0 ; i<6 ; i++ ) { | ||||
a += button(c, render(c)); | ||||
c++; | ||||
} | ||||
a += '</tr>'; | ||||
} | ||||
span = 6; | ||||
} | ||||
else { | ||||
// Minutes and seconds | ||||
a += '<tr>'; | ||||
for (j=0 ; j<60 ; j+=10 ) { | ||||
a += button(j, render(j), 'range'); | ||||
} | ||||
a += '</tr>'; | ||||
// Slight hack to allow for the different number of columns | ||||
a += '</tbody></thead><table class="'+className+' '+className+'-nospace"><tbody>'; | ||||
var start = range !== null ? | ||||
range : | ||||
Math.floor( val / 10 )*10; | ||||
a += '<tr>'; | ||||
for (j=start+1 ; j<start+10 ; j++ ) { | ||||
a += button(j, render(j)); | ||||
} | ||||
a += '</tr>'; | ||||
span = 6; | ||||
} | ||||
container | ||||
.empty() | ||||
.append( | ||||
'<table class="'+className+'">'+ | ||||
'<thead><tr><th colspan="'+span+'">'+ | ||||
i18n[unit] + | ||||
'</th></tr></thead>'+ | ||||
'<tbody>'+ | ||||
a+ | ||||
'</tbody>'+ | ||||
'</table>' | ||||
); | ||||
}, | ||||
/** | ||||
* Create the options for the month and year | ||||
* | ||||
* @param {integer} year Year | ||||
* @param {integer} month Month (starting at 0) | ||||
* @private | ||||
*/ | ||||
_optionsTitle: function () { | ||||
var i18n = this.c.i18n; | ||||
var min = this.c.minDate; | ||||
var max = this.c.maxDate; | ||||
var minYear = min ? min.getFullYear() : null; | ||||
var maxYear = max ? max.getFullYear() : null; | ||||
var i = minYear !== null ? minYear : new Date().getFullYear() - this.c.yearRange; | ||||
var j = maxYear !== null ? maxYear : new Date().getFullYear() + this.c.yearRange; | ||||
this._options( 'month', this._range( 0, 11 ), i18n.months ); | ||||
this._options( 'year', this._range( i, j ) ); | ||||
}, | ||||
/** | ||||
* Simple two digit pad | ||||
* | ||||
* @param {integer} i Value that might need padding | ||||
* @return {string|integer} Padded value | ||||
* @private | ||||
*/ | ||||
_pad: function ( i ) { | ||||
return i<10 ? '0'+i : i; | ||||
}, | ||||
/** | ||||
* Position the calendar to look attached to the input element | ||||
* @private | ||||
*/ | ||||
_position: function () { | ||||
var offset = this.c.attachTo === 'input' ? this.dom.input.position() : this.dom.input.offset(); | ||||
var container = this.dom.container; | ||||
var inputHeight = this.dom.input.outerHeight(); | ||||
if (container.hasClass('inline')) { | ||||
container.insertAfter( this.dom.input ); | ||||
return; | ||||
} | ||||
if ( this.s.parts.date && this.s.parts.time && $(window).width() > 550 ) { | ||||
container.addClass('horizontal'); | ||||
} | ||||
else { | ||||
container.removeClass('horizontal'); | ||||
} | ||||
if(this.c.attachTo === 'input') { | ||||
container | ||||
.css( { | ||||
top: offset.top + inputHeight, | ||||
left: offset.left | ||||
} ) | ||||
.insertAfter( this.dom.input ); | ||||
} | ||||
else { | ||||
container | ||||
.css( { | ||||
top: offset.top + inputHeight, | ||||
left: offset.left | ||||
} ) | ||||
.appendTo( 'body' ); | ||||
} | ||||
var calHeight = container.outerHeight(); | ||||
var calWidth = container.outerWidth(); | ||||
var scrollTop = $(window).scrollTop(); | ||||
// Correct to the bottom | ||||
if ( offset.top + inputHeight + calHeight - scrollTop > $(window).height() ) { | ||||
var newTop = offset.top - calHeight; | ||||
container.css( 'top', newTop < 0 ? 0 : newTop ); | ||||
} | ||||
// Correct to the right | ||||
if ( calWidth + offset.left > $(window).width() ) { | ||||
var newLeft = $(window).width() - calWidth; | ||||
// Account for elements which are inside a position absolute element | ||||
if (this.c.attachTo === 'input') { | ||||
newLeft -= $(container).offsetParent().offset().left; | ||||
} | ||||
container.css( 'left', newLeft < 0 ? 0 : newLeft ); | ||||
} | ||||
}, | ||||
/** | ||||
* Create a simple array with a range of values | ||||
* | ||||
* @param {integer} start Start value (inclusive) | ||||
* @param {integer} end End value (inclusive) | ||||
* @param {integer} [inc=1] Increment value | ||||
* @return {array} Created array | ||||
* @private | ||||
*/ | ||||
_range: function ( start, end, inc ) { | ||||
var a = []; | ||||
if ( ! inc ) { | ||||
inc = 1; | ||||
} | ||||
for ( var i=start ; i<=end ; i+=inc ) { | ||||
a.push( i ); | ||||
} | ||||
return a; | ||||
}, | ||||
/** | ||||
* Redraw the calendar based on the display date - this is a destructive | ||||
* operation | ||||
* | ||||
* @private | ||||
*/ | ||||
_setCalander: function () { | ||||
if ( this.s.display ) { | ||||
this.dom.calendar | ||||
.empty() | ||||
.append( this._htmlMonth( | ||||
this.s.display.getUTCFullYear(), | ||||
this.s.display.getUTCMonth() | ||||
) ); | ||||
} | ||||
}, | ||||
/** | ||||
* Set the month and year for the calendar based on the current display date | ||||
* | ||||
* @private | ||||
*/ | ||||
_setTitle: function () { | ||||
this._optionSet( 'month', this.s.display.getUTCMonth() ); | ||||
this._optionSet( 'year', this.s.display.getUTCFullYear() ); | ||||
}, | ||||
/** | ||||
* Set the time based on the current value of the widget | ||||
* | ||||
* @private | ||||
*/ | ||||
_setTime: function () { | ||||
var that = this; | ||||
var d = this.s.d; | ||||
// luxon uses different method names so need to be able to call them. This happens a few time later in this method too | ||||
var luxDT = null | ||||
if (dateLib && dateLib == window.luxon) { | ||||
luxDT = dateLib.DateTime.fromJSDate(d); | ||||
} | ||||
var hours = luxDT != null | ||||
? luxDT.hour | ||||
: d | ||||
? d.getUTCHours() | ||||
: 0; | ||||
var allowed = function ( prop ) { // Backwards compt with `Increment` option | ||||
return that.c[prop+'Available'] ? | ||||
that.c[prop+'Available'] : | ||||
that._range( 0, 59, that.c[prop+'Increment'] ); | ||||
} | ||||
this._optionsTime( 'hours', this.s.parts.hours12 ? 12 : 24, hours, this.c.hoursAvailable ) | ||||
this._optionsTime( | ||||
'minutes', | ||||
60, | ||||
luxDT != null | ||||
? luxDT.minute | ||||
: d | ||||
? d.getUTCMinutes() | ||||
: 0, allowed('minutes'), | ||||
this.s.minutesRange | ||||
); | ||||
this._optionsTime( | ||||
'seconds', | ||||
60, | ||||
luxDT != null | ||||
? luxDT.second | ||||
: d | ||||
? d.getSeconds() | ||||
: 0, | ||||
allowed('seconds'), | ||||
this.s.secondsRange | ||||
); | ||||
}, | ||||
/** | ||||
* Show the widget and add events to the document required only while it | ||||
* is displayed | ||||
* | ||||
* @private | ||||
*/ | ||||
_show: function () { | ||||
var that = this; | ||||
var namespace = this.s.namespace; | ||||
this._position(); | ||||
// Need to reposition on scroll | ||||
$(window).on( 'scroll.'+namespace+' resize.'+namespace, function () { | ||||
that._position(); | ||||
} ); | ||||
$('div.DTE_Body_Content').on( 'scroll.'+namespace, function () { | ||||
that._position(); | ||||
} ); | ||||
$('div.dataTables_scrollBody').on( 'scroll.'+namespace, function () { | ||||
that._position(); | ||||
} ); | ||||
var offsetParent = this.dom.input[0].offsetParent; | ||||
if ( offsetParent !== document.body ) { | ||||
$(offsetParent).on( 'scroll.'+namespace, function () { | ||||
that._position(); | ||||
} ); | ||||
} | ||||
// On tab focus will move to a different field (no keyboard navigation | ||||
// in the date picker - this might need to be changed). | ||||
$(document).on( 'keydown.'+namespace, function (e) { | ||||
if ( | ||||
e.keyCode === 9 || // tab | ||||
e.keyCode === 27 || // esc | ||||
e.keyCode === 13 // return | ||||
) { | ||||
that._hide(); | ||||
} | ||||
} ); | ||||
// Hide if clicking outside of the widget - but in a different click | ||||
// event from the one that was used to trigger the show (bubble and | ||||
// inline) | ||||
setTimeout( function () { | ||||
$('body').on( 'click.'+namespace, function (e) { | ||||
var parents = $(e.target).parents(); | ||||
if ( ! parents.filter( that.dom.container ).length && e.target !== that.dom.input[0] ) { | ||||
that._hide(); | ||||
} | ||||
} ); | ||||
}, 10 ); | ||||
}, | ||||
/** | ||||
* Write the formatted string to the input element this control is attached | ||||
* to | ||||
* | ||||
* @private | ||||
*/ | ||||
_writeOutput: function ( focus ) { | ||||
var date = this.s.d; | ||||
var out = ''; | ||||
// Use moment, dayjs or luxon if possible - otherwise it must be ISO8601 (or the | ||||
// constructor would have thrown an error) | ||||
// luxon uses different method names so need to be able to call them. | ||||
if (date) { | ||||
out = dateLib && dateLib == window.luxon | ||||
? dateLib.DateTime.fromJSDate(this.s.d).toFormat(this.c.format) | ||||
: dateLib ? | ||||
dateLib.utc( date, undefined, this.c.locale, this.c.strict ).format( this.c.format ) : | ||||
date.getUTCFullYear() +'-'+ | ||||
this._pad(date.getUTCMonth() + 1) +'-'+ | ||||
this._pad(date.getUTCDate()); | ||||
} | ||||
this.dom.input | ||||
.val( out ) | ||||
.trigger('change', {write: date}); | ||||
if ( this.dom.input.attr('type') === 'hidden' ) { | ||||
this.val(out, false); | ||||
} | ||||
if ( focus ) { | ||||
this.dom.input.focus(); | ||||
} | ||||
} | ||||
} ); | ||||
/** | ||||
* Use a specificmoment compatible date library | ||||
*/ | ||||
DateTime.use = function (lib) { | ||||
dateLib = lib; | ||||
}; | ||||
/** | ||||
* For generating unique namespaces | ||||
* | ||||
* @type {Number} | ||||
* @private | ||||
*/ | ||||
DateTime._instance = 0; | ||||
/** | ||||
* Defaults for the date time picker | ||||
* | ||||
* @type {Object} | ||||
*/ | ||||
DateTime.defaults = { | ||||
attachTo: 'body', | ||||
buttons: { | ||||
clear: false, | ||||
today: false | ||||
}, | ||||
// Not documented - could be an internal property | ||||
classPrefix: 'dt-datetime', | ||||
// function or array of ints | ||||
disableDays: null, | ||||
// first day of the week (0: Sunday, 1: Monday, etc) | ||||
firstDay: 1, | ||||
format: 'YYYY-MM-DD', | ||||
hoursAvailable: null, | ||||
i18n: { | ||||
clear: 'Clear', | ||||
previous: 'Previous', | ||||
next: 'Next', | ||||
months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], | ||||
weekdays: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ], | ||||
amPm: [ 'am', 'pm' ], | ||||
hours: 'Hour', | ||||
minutes: 'Minute', | ||||
seconds: 'Second', | ||||
unknown: '-', | ||||
today: 'Today' | ||||
}, | ||||
maxDate: null, | ||||
minDate: null, | ||||
minutesAvailable: null, | ||||
minutesIncrement: 1, // deprecated | ||||
strict: true, | ||||
locale: 'en', | ||||
onChange: function () {}, | ||||
secondsAvailable: null, | ||||
secondsIncrement: 1, // deprecated | ||||
// show the ISO week number at the head of the row | ||||
showWeekNumber: false, | ||||
// overruled by max / min date | ||||
yearRange: 25 | ||||
}; | ||||
DateTime.version = '1.1.2'; | ||||
// Global export - if no conflicts | ||||
if (! window.DateTime) { | ||||
window.DateTime = DateTime; | ||||
} | ||||
// Make available via jQuery | ||||
$.fn.dtDateTime = function (options) { | ||||
return this.each(function() { | ||||
new DateTime(this, options); | ||||
}); | ||||
} | ||||
// Attach to DataTables if present | ||||
if ($.fn.dataTable) { | ||||
$.fn.dataTable.DateTime = DateTime; | ||||
$.fn.DataTable.DateTime = DateTime; | ||||
if ($.fn.dataTable.Editor) { | ||||
$.fn.dataTable.Editor.DateTime = DateTime; | ||||
} | ||||
} | ||||
return DateTime; | ||||
})); | ||||