dataTables.dateTime.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564
  1. /*! DateTime picker for DataTables.net v1.1.2
  2. *
  3. * © SpryMedia Ltd, all rights reserved.
  4. * License: MIT datatables.net/license/mit
  5. */
  6. /**
  7. * @summary DateTime picker for DataTables.net
  8. * @version 1.1.2
  9. * @file dataTables.dateTime.js
  10. * @author SpryMedia Ltd
  11. * @contact www.datatables.net/contact
  12. */
  13. (function( factory ){
  14. if ( typeof define === 'function' && define.amd ) {
  15. // AMD
  16. define( ['jquery'], function ( $ ) {
  17. return factory( $, window, document );
  18. } );
  19. }
  20. else if ( typeof exports === 'object' ) {
  21. // CommonJS
  22. module.exports = function (root, $) {
  23. if ( ! root ) {
  24. root = window;
  25. }
  26. return factory( $, root, root.document );
  27. };
  28. }
  29. else {
  30. // Browser
  31. factory( jQuery, window, document );
  32. }
  33. }(function( $, window, document, undefined ) {
  34. 'use strict';
  35. // Supported formatting and parsing libraries:
  36. // * Moment
  37. // * Luxon
  38. // * DayJS
  39. var dateLib;
  40. /*
  41. * This file provides a DateTime GUI picker (calendar and time input). Only the
  42. * format YYYY-MM-DD is supported without additional software, but the end user
  43. * experience can be greatly enhanced by including the momentjs, dayjs or luxon library
  44. * which provide date / time parsing and formatting options.
  45. *
  46. * This functionality is required because the HTML5 date and datetime input
  47. * types are not widely supported in desktop browsers.
  48. *
  49. * Constructed by using:
  50. *
  51. * new DateTime( input, opts )
  52. *
  53. * where `input` is the HTML input element to use and `opts` is an object of
  54. * options based on the `DateTime.defaults` object.
  55. */
  56. var DateTime = function ( input, opts ) {
  57. // Attempt to auto detect the formatting library (if there is one). Having it in
  58. // the constructor allows load order independence.
  59. if (typeof dateLib === 'undefined') {
  60. dateLib = window.moment
  61. ? window.moment
  62. : window.dayjs
  63. ? window.dayjs
  64. : window.luxon
  65. ? window.luxon
  66. : null;
  67. }
  68. this.c = $.extend( true, {}, DateTime.defaults, opts );
  69. var classPrefix = this.c.classPrefix;
  70. var i18n = this.c.i18n;
  71. // Only IS8601 dates are supported without moment, dayjs or luxon
  72. if ( ! dateLib && this.c.format !== 'YYYY-MM-DD' ) {
  73. throw "DateTime: Without momentjs, dayjs or luxon only the format 'YYYY-MM-DD' can be used";
  74. }
  75. // Min and max need to be `Date` objects in the config
  76. if (typeof this.c.minDate === 'string') {
  77. this.c.minDate = new Date(this.c.minDate);
  78. }
  79. if (typeof this.c.maxDate === 'string') {
  80. this.c.maxDate = new Date(this.c.maxDate);
  81. }
  82. var timeBlock = function ( type ) {
  83. return '<div class="'+classPrefix+'-timeblock">'+
  84. '</div>';
  85. };
  86. var gap = function () {
  87. return '<span>:</span>';
  88. };
  89. // DOM structure
  90. var structure = $(
  91. '<div class="'+classPrefix+'">'+
  92. '<div class="'+classPrefix+'-date">'+
  93. '<div class="'+classPrefix+'-title">'+
  94. '<div class="'+classPrefix+'-iconLeft">'+
  95. '<button type="button" title="'+i18n.previous+'">'+i18n.previous+'</button>'+
  96. '</div>'+
  97. '<div class="'+classPrefix+'-iconRight">'+
  98. '<button type="button" title="'+i18n.next+'">'+i18n.next+'</button>'+
  99. '</div>'+
  100. '<div class="'+classPrefix+'-label">'+
  101. '<span></span>'+
  102. '<select class="'+classPrefix+'-month"></select>'+
  103. '</div>'+
  104. '<div class="'+classPrefix+'-label">'+
  105. '<span></span>'+
  106. '<select class="'+classPrefix+'-year"></select>'+
  107. '</div>'+
  108. '</div>'+
  109. '<div class="'+classPrefix+'-buttons">'+
  110. '<a class="'+classPrefix+'-clear">'+i18n.clear+'</a>'+
  111. '<a class="'+classPrefix+'-today">'+i18n.today+'</a>'+
  112. '</div>'+
  113. '<div class="'+classPrefix+'-calendar"></div>'+
  114. '</div>'+
  115. '<div class="'+classPrefix+'-time">'+
  116. '<div class="'+classPrefix+'-hours"></div>'+
  117. '<div class="'+classPrefix+'-minutes"></div>'+
  118. '<div class="'+classPrefix+'-seconds"></div>'+
  119. '</div>'+
  120. '<div class="'+classPrefix+'-error"></div>'+
  121. '</div>'
  122. );
  123. this.dom = {
  124. container: structure,
  125. date: structure.find( '.'+classPrefix+'-date' ),
  126. title: structure.find( '.'+classPrefix+'-title' ),
  127. calendar: structure.find( '.'+classPrefix+'-calendar' ),
  128. time: structure.find( '.'+classPrefix+'-time' ),
  129. error: structure.find( '.'+classPrefix+'-error' ),
  130. buttons: structure.find( '.'+classPrefix+'-buttons' ),
  131. clear: structure.find( '.'+classPrefix+'-clear' ),
  132. today: structure.find( '.'+classPrefix+'-today' ),
  133. input: $(input)
  134. };
  135. this.s = {
  136. /** @type {Date} Date value that the picker has currently selected */
  137. d: null,
  138. /** @type {Date} Date of the calendar - might not match the value */
  139. display: null,
  140. /** @type {number} Used to select minutes in a range where the range base is itself unavailable */
  141. minutesRange: null,
  142. /** @type {number} Used to select minutes in a range where the range base is itself unavailable */
  143. secondsRange: null,
  144. /** @type {String} Unique namespace string for this instance */
  145. namespace: 'dateime-'+(DateTime._instance++),
  146. /** @type {Object} Parts of the picker that should be shown */
  147. parts: {
  148. date: this.c.format.match( /[YMD]|L(?!T)|l/ ) !== null,
  149. time: this.c.format.match( /[Hhm]|LT|LTS/ ) !== null,
  150. seconds: this.c.format.indexOf( 's' ) !== -1,
  151. hours12: this.c.format.match( /[haA]/ ) !== null
  152. }
  153. };
  154. this.dom.container
  155. .append( this.dom.date )
  156. .append( this.dom.time )
  157. .append( this.dom.error );
  158. this.dom.date
  159. .append( this.dom.title )
  160. .append( this.dom.buttons )
  161. .append( this.dom.calendar );
  162. this._constructor();
  163. };
  164. $.extend( DateTime.prototype, {
  165. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  166. * Public
  167. */
  168. /**
  169. * Destroy the control
  170. */
  171. destroy: function () {
  172. this._hide(true);
  173. this.dom.container.off().empty();
  174. this.dom.input
  175. .removeAttr('autocomplete')
  176. .off('.datetime');
  177. },
  178. errorMsg: function ( msg ) {
  179. var error = this.dom.error;
  180. if ( msg ) {
  181. error.html( msg );
  182. }
  183. else {
  184. error.empty();
  185. }
  186. return this;
  187. },
  188. hide: function () {
  189. this._hide();
  190. return this;
  191. },
  192. max: function ( date ) {
  193. this.c.maxDate = typeof date === 'string'
  194. ? new Date(date)
  195. : date;
  196. this._optionsTitle();
  197. this._setCalander();
  198. return this;
  199. },
  200. min: function ( date ) {
  201. this.c.minDate = typeof date === 'string'
  202. ? new Date(date)
  203. : date;
  204. this._optionsTitle();
  205. this._setCalander();
  206. return this;
  207. },
  208. /**
  209. * Check if an element belongs to this control
  210. *
  211. * @param {node} node Element to check
  212. * @return {boolean} true if owned by this control, false otherwise
  213. */
  214. owns: function ( node ) {
  215. return $(node).parents().filter( this.dom.container ).length > 0;
  216. },
  217. /**
  218. * Get / set the value
  219. *
  220. * @param {string|Date} set Value to set
  221. * @param {boolean} [write=true] Flag to indicate if the formatted value
  222. * should be written into the input element
  223. */
  224. val: function ( set, write ) {
  225. if ( set === undefined ) {
  226. return this.s.d;
  227. }
  228. if ( set instanceof Date ) {
  229. this.s.d = this._dateToUtc( set );
  230. }
  231. else if ( set === null || set === '' ) {
  232. this.s.d = null;
  233. }
  234. else if ( set === '--now' ) {
  235. this.s.d = new Date();
  236. }
  237. else if ( typeof set === 'string' ) {
  238. // luxon uses different method names so need to be able to call them
  239. if(dateLib && dateLib == window.luxon) {
  240. var luxDT = dateLib.DateTime.fromFormat(set, this.c.format)
  241. this.s.d = luxDT.isValid ? luxDT.toJSDate() : null;
  242. }
  243. else if ( dateLib ) {
  244. // Use moment, dayjs or luxon if possible (even for ISO8601 strings, since it
  245. // will correctly handle 0000-00-00 and the like)
  246. var m = dateLib.utc( set, this.c.format, this.c.locale, this.c.strict );
  247. this.s.d = m.isValid() ? m.toDate() : null;
  248. }
  249. else {
  250. // Else must be using ISO8601 without a date library (constructor would
  251. // have thrown an error otherwise)
  252. var match = set.match(/(\d{4})\-(\d{2})\-(\d{2})/ );
  253. this.s.d = match ?
  254. new Date( Date.UTC(match[1], match[2]-1, match[3]) ) :
  255. null;
  256. }
  257. }
  258. if ( write || write === undefined ) {
  259. if ( this.s.d ) {
  260. this._writeOutput();
  261. }
  262. else {
  263. // The input value was not valid...
  264. this.dom.input.val( set );
  265. }
  266. }
  267. // Need something to display
  268. this.s.display = this.s.d
  269. ? new Date( this.s.d.toString() )
  270. : new Date();
  271. // Set the day of the month to be 1 so changing between months doesn't
  272. // run into issues when going from day 31 to 28 (for example)
  273. this.s.display.setUTCDate( 1 );
  274. // Update the display elements for the new value
  275. this._setTitle();
  276. this._setCalander();
  277. this._setTime();
  278. return this;
  279. },
  280. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  281. * Constructor
  282. */
  283. /**
  284. * Build the control and assign initial event handlers
  285. *
  286. * @private
  287. */
  288. _constructor: function () {
  289. var that = this;
  290. var classPrefix = this.c.classPrefix;
  291. var last = this.dom.input.val();
  292. var onChange = function () {
  293. var curr = that.dom.input.val();
  294. if (curr !== last) {
  295. that.c.onChange.call( that, curr, that.s.d, that.dom.input );
  296. last = curr;
  297. }
  298. };
  299. if ( ! this.s.parts.date ) {
  300. this.dom.date.css( 'display', 'none' );
  301. }
  302. if ( ! this.s.parts.time ) {
  303. this.dom.time.css( 'display', 'none' );
  304. }
  305. if ( ! this.s.parts.seconds ) {
  306. this.dom.time.children('div.'+classPrefix+'-seconds').remove();
  307. this.dom.time.children('span').eq(1).remove();
  308. }
  309. if ( ! this.c.buttons.clear ) {
  310. this.dom.clear.css( 'display', 'none' );
  311. }
  312. if ( ! this.c.buttons.today ) {
  313. this.dom.today.css( 'display', 'none' );
  314. }
  315. // Render the options
  316. this._optionsTitle();
  317. $(document).on('i18n.dt', function (e, settings) {
  318. if (settings.oLanguage.datetime) {
  319. $.extend(true, that.c.i18n, settings.oLanguage.datetime);
  320. that._optionsTitle();
  321. }
  322. });
  323. // When attached to a hidden input, we always show the input picker, and
  324. // do so inline
  325. if (this.dom.input.attr('type') === 'hidden') {
  326. this.dom.container.addClass('inline');
  327. this.c.attachTo = 'input';
  328. this.val( this.dom.input.val(), false );
  329. this._show();
  330. }
  331. // Set the initial value
  332. if (last) {
  333. this.val( last, false );
  334. }
  335. // Trigger the display of the widget when clicking or focusing on the
  336. // input element
  337. this.dom.input
  338. .attr('autocomplete', 'off')
  339. .on('focus.datetime click.datetime', function () {
  340. // If already visible - don't do anything
  341. if ( that.dom.container.is(':visible') || that.dom.input.is(':disabled') ) {
  342. return;
  343. }
  344. // In case the value has changed by text
  345. that.val( that.dom.input.val(), false );
  346. that._show();
  347. } )
  348. .on('keyup.datetime', function () {
  349. // Update the calendar's displayed value as the user types
  350. if ( that.dom.container.is(':visible') ) {
  351. that.val( that.dom.input.val(), false );
  352. }
  353. } );
  354. // Main event handlers for input in the widget
  355. this.dom.container
  356. .on( 'change', 'select', function () {
  357. var select = $(this);
  358. var val = select.val();
  359. if ( select.hasClass(classPrefix+'-month') ) {
  360. // Month select
  361. that._correctMonth( that.s.display, val );
  362. that._setTitle();
  363. that._setCalander();
  364. }
  365. else if ( select.hasClass(classPrefix+'-year') ) {
  366. // Year select
  367. that.s.display.setUTCFullYear( val );
  368. that._setTitle();
  369. that._setCalander();
  370. }
  371. else if ( select.hasClass(classPrefix+'-hours') || select.hasClass(classPrefix+'-ampm') ) {
  372. // Hours - need to take account of AM/PM input if present
  373. if ( that.s.parts.hours12 ) {
  374. var hours = $(that.dom.container).find('.'+classPrefix+'-hours').val() * 1;
  375. var pm = $(that.dom.container).find('.'+classPrefix+'-ampm').val() === 'pm';
  376. that.s.d.setUTCHours( hours === 12 && !pm ?
  377. 0 :
  378. pm && hours !== 12 ?
  379. hours + 12 :
  380. hours
  381. );
  382. }
  383. else {
  384. that.s.d.setUTCHours( val );
  385. }
  386. that._setTime();
  387. that._writeOutput( true );
  388. onChange();
  389. }
  390. else if ( select.hasClass(classPrefix+'-minutes') ) {
  391. // Minutes select
  392. that.s.d.setUTCMinutes( val );
  393. that._setTime();
  394. that._writeOutput( true );
  395. onChange();
  396. }
  397. else if ( select.hasClass(classPrefix+'-seconds') ) {
  398. // Seconds select
  399. that.s.d.setSeconds( val );
  400. that._setTime();
  401. that._writeOutput( true );
  402. onChange();
  403. }
  404. that.dom.input.focus();
  405. that._position();
  406. } )
  407. .on( 'click', function (e) {
  408. var d = that.s.d;
  409. var nodeName = e.target.nodeName.toLowerCase();
  410. var target = nodeName === 'span' ?
  411. e.target.parentNode :
  412. e.target;
  413. nodeName = target.nodeName.toLowerCase();
  414. if ( nodeName === 'select' ) {
  415. return;
  416. }
  417. e.stopPropagation();
  418. if ( nodeName === 'a' ) {
  419. e.preventDefault();
  420. if ($(target).hasClass(classPrefix+'-clear')) {
  421. // Clear the value and don't change the display
  422. that.s.d = null;
  423. that.dom.input.val('');
  424. that._writeOutput();
  425. that._setCalander();
  426. that._setTime();
  427. onChange();
  428. }
  429. else if ($(target).hasClass(classPrefix+'-today')) {
  430. // Don't change the value, but jump to the month
  431. // containing today
  432. that.s.display = new Date();
  433. that._setTitle();
  434. that._setCalander();
  435. }
  436. }
  437. if ( nodeName === 'button' ) {
  438. var button = $(target);
  439. var parent = button.parent();
  440. if ( parent.hasClass('disabled') && ! parent.hasClass('range') ) {
  441. button.blur();
  442. return;
  443. }
  444. if ( parent.hasClass(classPrefix+'-iconLeft') ) {
  445. // Previous month
  446. that.s.display.setUTCMonth( that.s.display.getUTCMonth()-1 );
  447. that._setTitle();
  448. that._setCalander();
  449. that.dom.input.focus();
  450. }
  451. else if ( parent.hasClass(classPrefix+'-iconRight') ) {
  452. // Next month
  453. that._correctMonth( that.s.display, that.s.display.getUTCMonth()+1 );
  454. that._setTitle();
  455. that._setCalander();
  456. that.dom.input.focus();
  457. }
  458. else if ( button.parents('.'+classPrefix+'-time').length ) {
  459. var val = button.data('value');
  460. var unit = button.data('unit');
  461. d = that._needValue();
  462. if ( unit === 'minutes' ) {
  463. if ( parent.hasClass('disabled') && parent.hasClass('range') ) {
  464. that.s.minutesRange = val;
  465. that._setTime();
  466. return;
  467. }
  468. else {
  469. that.s.minutesRange = null;
  470. }
  471. }
  472. if ( unit === 'seconds' ) {
  473. if ( parent.hasClass('disabled') && parent.hasClass('range') ) {
  474. that.s.secondsRange = val;
  475. that._setTime();
  476. return;
  477. }
  478. else {
  479. that.s.secondsRange = null;
  480. }
  481. }
  482. // Specific to hours for 12h clock
  483. if ( val === 'am' ) {
  484. if ( d.getUTCHours() >= 12 ) {
  485. val = d.getUTCHours() - 12;
  486. }
  487. else {
  488. return;
  489. }
  490. }
  491. else if ( val === 'pm' ) {
  492. if ( d.getUTCHours() < 12 ) {
  493. val = d.getUTCHours() + 12;
  494. }
  495. else {
  496. return;
  497. }
  498. }
  499. var set = unit === 'hours' ?
  500. 'setUTCHours' :
  501. unit === 'minutes' ?
  502. 'setUTCMinutes' :
  503. 'setSeconds';
  504. d[set]( val );
  505. that._setTime();
  506. that._writeOutput( true );
  507. onChange();
  508. }
  509. else {
  510. // Calendar click
  511. d = that._needValue();
  512. // Can't be certain that the current day will exist in
  513. // the new month, and likewise don't know that the
  514. // new day will exist in the old month, But 1 always
  515. // does, so we can change the month without worry of a
  516. // recalculation being done automatically by `Date`
  517. d.setUTCDate( 1 );
  518. d.setUTCFullYear( button.data('year') );
  519. d.setUTCMonth( button.data('month') );
  520. d.setUTCDate( button.data('day') );
  521. that._writeOutput( true );
  522. // Don't hide if there is a time picker, since we want to
  523. // be able to select a time as well.
  524. if ( ! that.s.parts.time ) {
  525. // This is annoying but IE has some kind of async
  526. // behaviour with focus and the focus from the above
  527. // write would occur after this hide - resulting in the
  528. // calendar opening immediately
  529. setTimeout( function () {
  530. that._hide();
  531. }, 10 );
  532. }
  533. else {
  534. that._setCalander();
  535. }
  536. onChange();
  537. }
  538. }
  539. else {
  540. // Click anywhere else in the widget - return focus to the
  541. // input element
  542. that.dom.input.focus();
  543. }
  544. } );
  545. },
  546. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  547. * Private
  548. */
  549. /**
  550. * Compare the date part only of two dates - this is made super easy by the
  551. * toDateString method!
  552. *
  553. * @param {Date} a Date 1
  554. * @param {Date} b Date 2
  555. * @private
  556. */
  557. _compareDates: function( a, b ) {
  558. // Can't use toDateString as that converts to local time
  559. // luxon uses different method names so need to be able to call them
  560. return dateLib && dateLib == window.luxon
  561. ? dateLib.DateTime.fromJSDate(a).toISODate() === dateLib.DateTime.fromJSDate(b).toISODate()
  562. : this._dateToUtcString(a) === this._dateToUtcString(b);
  563. },
  564. /**
  565. * When changing month, take account of the fact that some months don't have
  566. * the same number of days. For example going from January to February you
  567. * can have the 31st of Jan selected and just add a month since the date
  568. * would still be 31, and thus drop you into March.
  569. *
  570. * @param {Date} date Date - will be modified
  571. * @param {integer} month Month to set
  572. * @private
  573. */
  574. _correctMonth: function ( date, month ) {
  575. var days = this._daysInMonth( date.getUTCFullYear(), month );
  576. var correctDays = date.getUTCDate() > days;
  577. date.setUTCMonth( month );
  578. if ( correctDays ) {
  579. date.setUTCDate( days );
  580. date.setUTCMonth( month );
  581. }
  582. },
  583. /**
  584. * Get the number of days in a method. Based on
  585. * http://stackoverflow.com/a/4881951 by Matti Virkkunen
  586. *
  587. * @param {integer} year Year
  588. * @param {integer} month Month (starting at 0)
  589. * @private
  590. */
  591. _daysInMonth: function ( year, month ) {
  592. //
  593. var isLeap = ((year % 4) === 0 && ((year % 100) !== 0 || (year % 400) === 0));
  594. var months = [31, (isLeap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  595. return months[month];
  596. },
  597. /**
  598. * Create a new date object which has the UTC values set to the local time.
  599. * This allows the local time to be used directly for the library which
  600. * always bases its calculations and display on UTC.
  601. *
  602. * @param {Date} s Date to "convert"
  603. * @return {Date} Shifted date
  604. */
  605. _dateToUtc: function ( s ) {
  606. return new Date( Date.UTC(
  607. s.getFullYear(), s.getMonth(), s.getDate(),
  608. s.getHours(), s.getMinutes(), s.getSeconds()
  609. ) );
  610. },
  611. /**
  612. * Create a UTC ISO8601 date part from a date object
  613. *
  614. * @param {Date} d Date to "convert"
  615. * @return {string} ISO formatted date
  616. */
  617. _dateToUtcString: function ( d ) {
  618. // luxon uses different method names so need to be able to call them
  619. return dateLib && dateLib == window.luxon
  620. ? dateLib.DateTime.fromJSDate(d).toISODate()
  621. : d.getUTCFullYear()+'-'+
  622. this._pad(d.getUTCMonth()+1)+'-'+
  623. this._pad(d.getUTCDate());
  624. },
  625. /**
  626. * Hide the control and remove events related to its display
  627. *
  628. * @private
  629. */
  630. _hide: function (destroy) {
  631. if (! destroy && this.dom.input.attr('type') === 'hidden') {
  632. return;
  633. }
  634. var namespace = this.s.namespace;
  635. this.dom.container.detach();
  636. $(window).off( '.'+namespace );
  637. $(document).off( 'keydown.'+namespace );
  638. $('div.dataTables_scrollBody').off( 'scroll.'+namespace );
  639. $('div.DTE_Body_Content').off( 'scroll.'+namespace );
  640. $('body').off( 'click.'+namespace );
  641. $(this.dom.input[0].offsetParent).off('.'+namespace);
  642. },
  643. /**
  644. * Convert a 24 hour value to a 12 hour value
  645. *
  646. * @param {integer} val 24 hour value
  647. * @return {integer} 12 hour value
  648. * @private
  649. */
  650. _hours24To12: function ( val ) {
  651. return val === 0 ?
  652. 12 :
  653. val > 12 ?
  654. val - 12 :
  655. val;
  656. },
  657. /**
  658. * Generate the HTML for a single day in the calendar - this is basically
  659. * and HTML cell with a button that has data attributes so we know what was
  660. * clicked on (if it is clicked on) and a bunch of classes for styling.
  661. *
  662. * @param {object} day Day object from the `_htmlMonth` method
  663. * @return {string} HTML cell
  664. */
  665. _htmlDay: function( day )
  666. {
  667. if ( day.empty ) {
  668. return '<td class="empty"></td>';
  669. }
  670. var classes = [ 'selectable' ];
  671. var classPrefix = this.c.classPrefix;
  672. if ( day.disabled ) {
  673. classes.push( 'disabled' );
  674. }
  675. if ( day.today ) {
  676. classes.push( 'now' );
  677. }
  678. if ( day.selected ) {
  679. classes.push( 'selected' );
  680. }
  681. return '<td data-day="' + day.day + '" class="' + classes.join(' ') + '">' +
  682. '<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" ' +'data-year="' + day.year + '" data-month="' + day.month + '" data-day="' + day.day + '">' +
  683. '<span>'+day.day+'</span>'+
  684. '</button>' +
  685. '</td>';
  686. },
  687. /**
  688. * Create the HTML for a month to be displayed in the calendar table.
  689. *
  690. * Based upon the logic used in Pikaday - MIT licensed
  691. * Copyright (c) 2014 David Bushell
  692. * https://github.com/dbushell/Pikaday
  693. *
  694. * @param {integer} year Year
  695. * @param {integer} month Month (starting at 0)
  696. * @return {string} Calendar month HTML
  697. * @private
  698. */
  699. _htmlMonth: function ( year, month ) {
  700. var now = this._dateToUtc( new Date() ),
  701. days = this._daysInMonth( year, month ),
  702. before = new Date( Date.UTC(year, month, 1) ).getUTCDay(),
  703. data = [],
  704. row = [];
  705. if ( this.c.firstDay > 0 ) {
  706. before -= this.c.firstDay;
  707. if (before < 0) {
  708. before += 7;
  709. }
  710. }
  711. var cells = days + before,
  712. after = cells;
  713. while ( after > 7 ) {
  714. after -= 7;
  715. }
  716. cells += 7 - after;
  717. var minDate = this.c.minDate;
  718. var maxDate = this.c.maxDate;
  719. if ( minDate ) {
  720. minDate.setUTCHours(0);
  721. minDate.setUTCMinutes(0);
  722. minDate.setSeconds(0);
  723. }
  724. if ( maxDate ) {
  725. maxDate.setUTCHours(23);
  726. maxDate.setUTCMinutes(59);
  727. maxDate.setSeconds(59);
  728. }
  729. for ( var i=0, r=0 ; i<cells ; i++ ) {
  730. var day = new Date( Date.UTC(year, month, 1 + (i - before)) ),
  731. selected = this.s.d ? this._compareDates(day, this.s.d) : false,
  732. today = this._compareDates(day, now),
  733. empty = i < before || i >= (days + before),
  734. disabled = (minDate && day < minDate) ||
  735. (maxDate && day > maxDate);
  736. var disableDays = this.c.disableDays;
  737. if ( Array.isArray( disableDays ) && $.inArray( day.getUTCDay(), disableDays ) !== -1 ) {
  738. disabled = true;
  739. }
  740. else if ( typeof disableDays === 'function' && disableDays( day ) === true ) {
  741. disabled = true;
  742. }
  743. var dayConfig = {
  744. day: 1 + (i - before),
  745. month: month,
  746. year: year,
  747. selected: selected,
  748. today: today,
  749. disabled: disabled,
  750. empty: empty
  751. };
  752. row.push( this._htmlDay(dayConfig) );
  753. if ( ++r === 7 ) {
  754. if ( this.c.showWeekNumber ) {
  755. row.unshift( this._htmlWeekOfYear(i - before, month, year) );
  756. }
  757. data.push( '<tr>'+row.join('')+'</tr>' );
  758. row = [];
  759. r = 0;
  760. }
  761. }
  762. var classPrefix = this.c.classPrefix;
  763. var className = classPrefix+'-table';
  764. if ( this.c.showWeekNumber ) {
  765. className += ' weekNumber';
  766. }
  767. // Show / hide month icons based on min/max
  768. if ( minDate ) {
  769. var underMin = minDate >= new Date( Date.UTC(year, month, 1, 0, 0, 0 ) );
  770. this.dom.title.find('div.'+classPrefix+'-iconLeft')
  771. .css( 'display', underMin ? 'none' : 'block' );
  772. }
  773. if ( maxDate ) {
  774. var overMax = maxDate < new Date( Date.UTC(year, month+1, 1, 0, 0, 0 ) );
  775. this.dom.title.find('div.'+classPrefix+'-iconRight')
  776. .css( 'display', overMax ? 'none' : 'block' );
  777. }
  778. return '<table class="'+className+'">' +
  779. '<thead>'+
  780. this._htmlMonthHead() +
  781. '</thead>'+
  782. '<tbody>'+
  783. data.join('') +
  784. '</tbody>'+
  785. '</table>';
  786. },
  787. /**
  788. * Create the calendar table's header (week days)
  789. *
  790. * @return {string} HTML cells for the row
  791. * @private
  792. */
  793. _htmlMonthHead: function () {
  794. var a = [];
  795. var firstDay = this.c.firstDay;
  796. var i18n = this.c.i18n;
  797. // Take account of the first day shift
  798. var dayName = function ( day ) {
  799. day += firstDay;
  800. while (day >= 7) {
  801. day -= 7;
  802. }
  803. return i18n.weekdays[day];
  804. };
  805. // Empty cell in the header
  806. if ( this.c.showWeekNumber ) {
  807. a.push( '<th></th>' );
  808. }
  809. for ( var i=0 ; i<7 ; i++ ) {
  810. a.push( '<th>'+dayName( i )+'</th>' );
  811. }
  812. return a.join('');
  813. },
  814. /**
  815. * Create a cell that contains week of the year - ISO8601
  816. *
  817. * Based on https://stackoverflow.com/questions/6117814/ and
  818. * http://techblog.procurios.nl/k/n618/news/view/33796/14863/
  819. *
  820. * @param {integer} d Day of month
  821. * @param {integer} m Month of year (zero index)
  822. * @param {integer} y Year
  823. * @return {string}
  824. * @private
  825. */
  826. _htmlWeekOfYear: function ( d, m, y ) {
  827. var date = new Date( y, m, d, 0, 0, 0, 0 );
  828. // First week of the year always has 4th January in it
  829. date.setDate( date.getDate() + 4 - (date.getDay() || 7) );
  830. var oneJan = new Date( y, 0, 1 );
  831. var weekNum = Math.ceil( ( ( (date - oneJan) / 86400000) + 1)/7 );
  832. return '<td class="'+this.c.classPrefix+'-week">' + weekNum + '</td>';
  833. },
  834. /**
  835. * Check if the instance has a date object value - it might be null.
  836. * If is doesn't set one to now.
  837. * @returns A Date object
  838. * @private
  839. */
  840. _needValue: function () {
  841. if ( ! this.s.d ) {
  842. this.s.d = this._dateToUtc( new Date() );
  843. }
  844. return this.s.d;
  845. },
  846. /**
  847. * Create option elements from a range in an array
  848. *
  849. * @param {string} selector Class name unique to the select element to use
  850. * @param {array} values Array of values
  851. * @param {array} [labels] Array of labels. If given must be the same
  852. * length as the values parameter.
  853. * @private
  854. */
  855. _options: function ( selector, values, labels ) {
  856. if ( ! labels ) {
  857. labels = values;
  858. }
  859. var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector);
  860. select.empty();
  861. for ( var i=0, ien=values.length ; i<ien ; i++ ) {
  862. select.append( '<option value="'+values[i]+'">'+labels[i]+'</option>' );
  863. }
  864. },
  865. /**
  866. * Set an option and update the option's span pair (since the select element
  867. * has opacity 0 for styling)
  868. *
  869. * @param {string} selector Class name unique to the select element to use
  870. * @param {*} val Value to set
  871. * @private
  872. */
  873. _optionSet: function ( selector, val ) {
  874. var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector);
  875. var span = select.parent().children('span');
  876. select.val( val );
  877. var selected = select.find('option:selected');
  878. span.html( selected.length !== 0 ?
  879. selected.text() :
  880. this.c.i18n.unknown
  881. );
  882. },
  883. /**
  884. * Create time options list.
  885. *
  886. * @param {string} unit Time unit - hours, minutes or seconds
  887. * @param {integer} count Count range - 12, 24 or 60
  888. * @param {integer} val Existing value for this unit
  889. * @param {integer[]} allowed Values allow for selection
  890. * @param {integer} range Override range
  891. * @private
  892. */
  893. _optionsTime: function ( unit, count, val, allowed, range ) {
  894. var classPrefix = this.c.classPrefix;
  895. var container = this.dom.container.find('div.'+classPrefix+'-'+unit);
  896. var i, j;
  897. var render = count === 12 ?
  898. function (i) { return i; } :
  899. this._pad;
  900. var classPrefix = this.c.classPrefix;
  901. var className = classPrefix+'-table';
  902. var i18n = this.c.i18n;
  903. if ( ! container.length ) {
  904. return;
  905. }
  906. var a = '';
  907. var span = 10;
  908. var button = function (value, label, className) {
  909. // Shift the value for PM
  910. if ( count === 12 && typeof value === 'number' ) {
  911. if (val >= 12 ) {
  912. value += 12;
  913. }
  914. if (value == 12) {
  915. value = 0;
  916. }
  917. else if (value == 24) {
  918. value = 12;
  919. }
  920. }
  921. var selected = val === value || (value === 'am' && val < 12) || (value === 'pm' && val >= 12) ?
  922. 'selected' :
  923. '';
  924. if (allowed && $.inArray(value, allowed) === -1) {
  925. selected += ' disabled';
  926. }
  927. if ( className ) {
  928. selected += ' '+className;
  929. }
  930. return '<td class="selectable '+selected+'">' +
  931. '<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" data-unit="'+unit+'" data-value="'+value+ '">' +
  932. '<span>'+label+'</span>'+
  933. '</button>' +
  934. '</td>';
  935. }
  936. if ( count === 12 ) {
  937. // Hours with AM/PM
  938. a += '<tr>';
  939. for ( i=1 ; i<=6 ; i++ ) {
  940. a += button(i, render(i));
  941. }
  942. a += button('am', i18n.amPm[0]);
  943. a += '</tr>';
  944. a += '<tr>';
  945. for ( i=7 ; i<=12 ; i++ ) {
  946. a += button(i, render(i));
  947. }
  948. a += button('pm', i18n.amPm[1]);
  949. a += '</tr>';
  950. span = 7;
  951. }
  952. else if ( count === 24 ) {
  953. // Hours - 24
  954. var c = 0;
  955. for (j=0 ; j<4 ; j++ ) {
  956. a += '<tr>';
  957. for ( i=0 ; i<6 ; i++ ) {
  958. a += button(c, render(c));
  959. c++;
  960. }
  961. a += '</tr>';
  962. }
  963. span = 6;
  964. }
  965. else {
  966. // Minutes and seconds
  967. a += '<tr>';
  968. for (j=0 ; j<60 ; j+=10 ) {
  969. a += button(j, render(j), 'range');
  970. }
  971. a += '</tr>';
  972. // Slight hack to allow for the different number of columns
  973. a += '</tbody></thead><table class="'+className+' '+className+'-nospace"><tbody>';
  974. var start = range !== null ?
  975. range :
  976. Math.floor( val / 10 )*10;
  977. a += '<tr>';
  978. for (j=start+1 ; j<start+10 ; j++ ) {
  979. a += button(j, render(j));
  980. }
  981. a += '</tr>';
  982. span = 6;
  983. }
  984. container
  985. .empty()
  986. .append(
  987. '<table class="'+className+'">'+
  988. '<thead><tr><th colspan="'+span+'">'+
  989. i18n[unit] +
  990. '</th></tr></thead>'+
  991. '<tbody>'+
  992. a+
  993. '</tbody>'+
  994. '</table>'
  995. );
  996. },
  997. /**
  998. * Create the options for the month and year
  999. *
  1000. * @param {integer} year Year
  1001. * @param {integer} month Month (starting at 0)
  1002. * @private
  1003. */
  1004. _optionsTitle: function () {
  1005. var i18n = this.c.i18n;
  1006. var min = this.c.minDate;
  1007. var max = this.c.maxDate;
  1008. var minYear = min ? min.getFullYear() : null;
  1009. var maxYear = max ? max.getFullYear() : null;
  1010. var i = minYear !== null ? minYear : new Date().getFullYear() - this.c.yearRange;
  1011. var j = maxYear !== null ? maxYear : new Date().getFullYear() + this.c.yearRange;
  1012. this._options( 'month', this._range( 0, 11 ), i18n.months );
  1013. this._options( 'year', this._range( i, j ) );
  1014. },
  1015. /**
  1016. * Simple two digit pad
  1017. *
  1018. * @param {integer} i Value that might need padding
  1019. * @return {string|integer} Padded value
  1020. * @private
  1021. */
  1022. _pad: function ( i ) {
  1023. return i<10 ? '0'+i : i;
  1024. },
  1025. /**
  1026. * Position the calendar to look attached to the input element
  1027. * @private
  1028. */
  1029. _position: function () {
  1030. var offset = this.c.attachTo === 'input' ? this.dom.input.position() : this.dom.input.offset();
  1031. var container = this.dom.container;
  1032. var inputHeight = this.dom.input.outerHeight();
  1033. if (container.hasClass('inline')) {
  1034. container.insertAfter( this.dom.input );
  1035. return;
  1036. }
  1037. if ( this.s.parts.date && this.s.parts.time && $(window).width() > 550 ) {
  1038. container.addClass('horizontal');
  1039. }
  1040. else {
  1041. container.removeClass('horizontal');
  1042. }
  1043. if(this.c.attachTo === 'input') {
  1044. container
  1045. .css( {
  1046. top: offset.top + inputHeight,
  1047. left: offset.left
  1048. } )
  1049. .insertAfter( this.dom.input );
  1050. }
  1051. else {
  1052. container
  1053. .css( {
  1054. top: offset.top + inputHeight,
  1055. left: offset.left
  1056. } )
  1057. .appendTo( 'body' );
  1058. }
  1059. var calHeight = container.outerHeight();
  1060. var calWidth = container.outerWidth();
  1061. var scrollTop = $(window).scrollTop();
  1062. // Correct to the bottom
  1063. if ( offset.top + inputHeight + calHeight - scrollTop > $(window).height() ) {
  1064. var newTop = offset.top - calHeight;
  1065. container.css( 'top', newTop < 0 ? 0 : newTop );
  1066. }
  1067. // Correct to the right
  1068. if ( calWidth + offset.left > $(window).width() ) {
  1069. var newLeft = $(window).width() - calWidth;
  1070. // Account for elements which are inside a position absolute element
  1071. if (this.c.attachTo === 'input') {
  1072. newLeft -= $(container).offsetParent().offset().left;
  1073. }
  1074. container.css( 'left', newLeft < 0 ? 0 : newLeft );
  1075. }
  1076. },
  1077. /**
  1078. * Create a simple array with a range of values
  1079. *
  1080. * @param {integer} start Start value (inclusive)
  1081. * @param {integer} end End value (inclusive)
  1082. * @param {integer} [inc=1] Increment value
  1083. * @return {array} Created array
  1084. * @private
  1085. */
  1086. _range: function ( start, end, inc ) {
  1087. var a = [];
  1088. if ( ! inc ) {
  1089. inc = 1;
  1090. }
  1091. for ( var i=start ; i<=end ; i+=inc ) {
  1092. a.push( i );
  1093. }
  1094. return a;
  1095. },
  1096. /**
  1097. * Redraw the calendar based on the display date - this is a destructive
  1098. * operation
  1099. *
  1100. * @private
  1101. */
  1102. _setCalander: function () {
  1103. if ( this.s.display ) {
  1104. this.dom.calendar
  1105. .empty()
  1106. .append( this._htmlMonth(
  1107. this.s.display.getUTCFullYear(),
  1108. this.s.display.getUTCMonth()
  1109. ) );
  1110. }
  1111. },
  1112. /**
  1113. * Set the month and year for the calendar based on the current display date
  1114. *
  1115. * @private
  1116. */
  1117. _setTitle: function () {
  1118. this._optionSet( 'month', this.s.display.getUTCMonth() );
  1119. this._optionSet( 'year', this.s.display.getUTCFullYear() );
  1120. },
  1121. /**
  1122. * Set the time based on the current value of the widget
  1123. *
  1124. * @private
  1125. */
  1126. _setTime: function () {
  1127. var that = this;
  1128. var d = this.s.d;
  1129. // luxon uses different method names so need to be able to call them. This happens a few time later in this method too
  1130. var luxDT = null
  1131. if (dateLib && dateLib == window.luxon) {
  1132. luxDT = dateLib.DateTime.fromJSDate(d);
  1133. }
  1134. var hours = luxDT != null
  1135. ? luxDT.hour
  1136. : d
  1137. ? d.getUTCHours()
  1138. : 0;
  1139. var allowed = function ( prop ) { // Backwards compt with `Increment` option
  1140. return that.c[prop+'Available'] ?
  1141. that.c[prop+'Available'] :
  1142. that._range( 0, 59, that.c[prop+'Increment'] );
  1143. }
  1144. this._optionsTime( 'hours', this.s.parts.hours12 ? 12 : 24, hours, this.c.hoursAvailable )
  1145. this._optionsTime(
  1146. 'minutes',
  1147. 60,
  1148. luxDT != null
  1149. ? luxDT.minute
  1150. : d
  1151. ? d.getUTCMinutes()
  1152. : 0, allowed('minutes'),
  1153. this.s.minutesRange
  1154. );
  1155. this._optionsTime(
  1156. 'seconds',
  1157. 60,
  1158. luxDT != null
  1159. ? luxDT.second
  1160. : d
  1161. ? d.getSeconds()
  1162. : 0,
  1163. allowed('seconds'),
  1164. this.s.secondsRange
  1165. );
  1166. },
  1167. /**
  1168. * Show the widget and add events to the document required only while it
  1169. * is displayed
  1170. *
  1171. * @private
  1172. */
  1173. _show: function () {
  1174. var that = this;
  1175. var namespace = this.s.namespace;
  1176. this._position();
  1177. // Need to reposition on scroll
  1178. $(window).on( 'scroll.'+namespace+' resize.'+namespace, function () {
  1179. that._position();
  1180. } );
  1181. $('div.DTE_Body_Content').on( 'scroll.'+namespace, function () {
  1182. that._position();
  1183. } );
  1184. $('div.dataTables_scrollBody').on( 'scroll.'+namespace, function () {
  1185. that._position();
  1186. } );
  1187. var offsetParent = this.dom.input[0].offsetParent;
  1188. if ( offsetParent !== document.body ) {
  1189. $(offsetParent).on( 'scroll.'+namespace, function () {
  1190. that._position();
  1191. } );
  1192. }
  1193. // On tab focus will move to a different field (no keyboard navigation
  1194. // in the date picker - this might need to be changed).
  1195. $(document).on( 'keydown.'+namespace, function (e) {
  1196. if (
  1197. e.keyCode === 9 || // tab
  1198. e.keyCode === 27 || // esc
  1199. e.keyCode === 13 // return
  1200. ) {
  1201. that._hide();
  1202. }
  1203. } );
  1204. // Hide if clicking outside of the widget - but in a different click
  1205. // event from the one that was used to trigger the show (bubble and
  1206. // inline)
  1207. setTimeout( function () {
  1208. $('body').on( 'click.'+namespace, function (e) {
  1209. var parents = $(e.target).parents();
  1210. if ( ! parents.filter( that.dom.container ).length && e.target !== that.dom.input[0] ) {
  1211. that._hide();
  1212. }
  1213. } );
  1214. }, 10 );
  1215. },
  1216. /**
  1217. * Write the formatted string to the input element this control is attached
  1218. * to
  1219. *
  1220. * @private
  1221. */
  1222. _writeOutput: function ( focus ) {
  1223. var date = this.s.d;
  1224. var out = '';
  1225. // Use moment, dayjs or luxon if possible - otherwise it must be ISO8601 (or the
  1226. // constructor would have thrown an error)
  1227. // luxon uses different method names so need to be able to call them.
  1228. if (date) {
  1229. out = dateLib && dateLib == window.luxon
  1230. ? dateLib.DateTime.fromJSDate(this.s.d).toFormat(this.c.format)
  1231. : dateLib ?
  1232. dateLib.utc( date, undefined, this.c.locale, this.c.strict ).format( this.c.format ) :
  1233. date.getUTCFullYear() +'-'+
  1234. this._pad(date.getUTCMonth() + 1) +'-'+
  1235. this._pad(date.getUTCDate());
  1236. }
  1237. this.dom.input
  1238. .val( out )
  1239. .trigger('change', {write: date});
  1240. if ( this.dom.input.attr('type') === 'hidden' ) {
  1241. this.val(out, false);
  1242. }
  1243. if ( focus ) {
  1244. this.dom.input.focus();
  1245. }
  1246. }
  1247. } );
  1248. /**
  1249. * Use a specificmoment compatible date library
  1250. */
  1251. DateTime.use = function (lib) {
  1252. dateLib = lib;
  1253. };
  1254. /**
  1255. * For generating unique namespaces
  1256. *
  1257. * @type {Number}
  1258. * @private
  1259. */
  1260. DateTime._instance = 0;
  1261. /**
  1262. * Defaults for the date time picker
  1263. *
  1264. * @type {Object}
  1265. */
  1266. DateTime.defaults = {
  1267. attachTo: 'body',
  1268. buttons: {
  1269. clear: false,
  1270. today: false
  1271. },
  1272. // Not documented - could be an internal property
  1273. classPrefix: 'dt-datetime',
  1274. // function or array of ints
  1275. disableDays: null,
  1276. // first day of the week (0: Sunday, 1: Monday, etc)
  1277. firstDay: 1,
  1278. format: 'YYYY-MM-DD',
  1279. hoursAvailable: null,
  1280. i18n: {
  1281. clear: 'Clear',
  1282. previous: 'Previous',
  1283. next: 'Next',
  1284. months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
  1285. weekdays: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
  1286. amPm: [ 'am', 'pm' ],
  1287. hours: 'Hour',
  1288. minutes: 'Minute',
  1289. seconds: 'Second',
  1290. unknown: '-',
  1291. today: 'Today'
  1292. },
  1293. maxDate: null,
  1294. minDate: null,
  1295. minutesAvailable: null,
  1296. minutesIncrement: 1, // deprecated
  1297. strict: true,
  1298. locale: 'en',
  1299. onChange: function () {},
  1300. secondsAvailable: null,
  1301. secondsIncrement: 1, // deprecated
  1302. // show the ISO week number at the head of the row
  1303. showWeekNumber: false,
  1304. // overruled by max / min date
  1305. yearRange: 25
  1306. };
  1307. DateTime.version = '1.1.2';
  1308. // Global export - if no conflicts
  1309. if (! window.DateTime) {
  1310. window.DateTime = DateTime;
  1311. }
  1312. // Make available via jQuery
  1313. $.fn.dtDateTime = function (options) {
  1314. return this.each(function() {
  1315. new DateTime(this, options);
  1316. });
  1317. }
  1318. // Attach to DataTables if present
  1319. if ($.fn.dataTable) {
  1320. $.fn.dataTable.DateTime = DateTime;
  1321. $.fn.DataTable.DateTime = DateTime;
  1322. if ($.fn.dataTable.Editor) {
  1323. $.fn.dataTable.Editor.DateTime = DateTime;
  1324. }
  1325. }
  1326. return DateTime;
  1327. }));