bootstrap-datetimepicker.js 103 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611
  1. /*!
  2. * Bootstrap Datetime Picker v4.17.49
  3. * Copyright 2015-2020 Jonathan Peterson
  4. * Licensed under MIT (https://github.com/Eonasdan/bootstrap-datetimepicker/blob/master/LICENSE)
  5. */
  6. /*global define:false */
  7. /*global exports:false */
  8. /*global require:false */
  9. /*global jQuery:false */
  10. /*global moment:false */
  11. (function (factory) {
  12. 'use strict';
  13. if (typeof define === 'function' && define.amd) {
  14. // AMD is used - Register as an anonymous module.
  15. define(['jquery', 'moment'], factory);
  16. } else if (typeof exports === 'object') {
  17. module.exports = factory(require('jquery'), require('moment'));
  18. } else {
  19. // Neither AMD nor CommonJS used. Use global variables.
  20. if (typeof jQuery === 'undefined') {
  21. throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
  22. }
  23. if (typeof moment === 'undefined') {
  24. throw 'bootstrap-datetimepicker requires Moment.js to be loaded first';
  25. }
  26. factory(jQuery, moment);
  27. }
  28. }(function ($, moment) {
  29. 'use strict';
  30. if (!moment) {
  31. throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first');
  32. }
  33. var dateTimePicker = function (element, options) {
  34. var picker = {},
  35. date,
  36. viewDate,
  37. unset = true,
  38. input,
  39. component = false,
  40. widget = false,
  41. use24Hours,
  42. minViewModeNumber = 0,
  43. actualFormat,
  44. parseFormats,
  45. currentViewMode,
  46. datePickerModes = [
  47. {
  48. clsName: 'days',
  49. navFnc: 'M',
  50. navStep: 1
  51. },
  52. {
  53. clsName: 'months',
  54. navFnc: 'y',
  55. navStep: 1
  56. },
  57. {
  58. clsName: 'years',
  59. navFnc: 'y',
  60. navStep: 10
  61. },
  62. {
  63. clsName: 'decades',
  64. navFnc: 'y',
  65. navStep: 100
  66. }
  67. ],
  68. viewModes = ['days', 'months', 'years', 'decades'],
  69. verticalModes = ['top', 'bottom', 'auto'],
  70. horizontalModes = ['left', 'right', 'auto'],
  71. toolbarPlacements = ['default', 'top', 'bottom'],
  72. keyMap = {
  73. 'up': 38,
  74. 38: 'up',
  75. 'down': 40,
  76. 40: 'down',
  77. 'left': 37,
  78. 37: 'left',
  79. 'right': 39,
  80. 39: 'right',
  81. 'tab': 9,
  82. 9: 'tab',
  83. 'escape': 27,
  84. 27: 'escape',
  85. 'enter': 13,
  86. 13: 'enter',
  87. 'pageUp': 33,
  88. 33: 'pageUp',
  89. 'pageDown': 34,
  90. 34: 'pageDown',
  91. 'shift': 16,
  92. 16: 'shift',
  93. 'control': 17,
  94. 17: 'control',
  95. 'space': 32,
  96. 32: 'space',
  97. 't': 84,
  98. 84: 't',
  99. 'delete': 46,
  100. 46: 'delete'
  101. },
  102. keyState = {},
  103. /********************************************************************************
  104. *
  105. * Private functions
  106. *
  107. ********************************************************************************/
  108. hasTimeZone = function () {
  109. return moment.tz !== undefined && options.timeZone !== undefined && options.timeZone !== null && options.timeZone !== '';
  110. },
  111. getMoment = function (d) {
  112. var returnMoment;
  113. if (d === undefined || d === null) {
  114. returnMoment = moment(); //TODO should this use format? and locale?
  115. } else if (moment.isDate(d) || moment.isMoment(d)) {
  116. // If the date that is passed in is already a Date() or moment() object,
  117. // pass it directly to moment.
  118. returnMoment = moment(d);
  119. } else if (hasTimeZone()) { // There is a string to parse and a default time zone
  120. // parse with the tz function which takes a default time zone if it is not in the format string
  121. returnMoment = moment.tz(d, parseFormats, options.useStrict, options.timeZone);
  122. } else {
  123. returnMoment = moment(d, parseFormats, options.useStrict);
  124. }
  125. if (hasTimeZone()) {
  126. returnMoment.tz(options.timeZone);
  127. }
  128. return returnMoment;
  129. },
  130. isEnabled = function (granularity) {
  131. if (typeof granularity !== 'string' || granularity.length > 1) {
  132. throw new TypeError('isEnabled expects a single character string parameter');
  133. }
  134. switch (granularity) {
  135. case 'y':
  136. return actualFormat.indexOf('Y') !== -1;
  137. case 'M':
  138. return actualFormat.indexOf('M') !== -1;
  139. case 'd':
  140. return actualFormat.toLowerCase().indexOf('d') !== -1;
  141. case 'h':
  142. case 'H':
  143. return actualFormat.toLowerCase().indexOf('h') !== -1;
  144. case 'm':
  145. return actualFormat.indexOf('m') !== -1;
  146. case 's':
  147. return actualFormat.indexOf('s') !== -1;
  148. default:
  149. return false;
  150. }
  151. },
  152. hasTime = function () {
  153. return (isEnabled('h') || isEnabled('m') || isEnabled('s'));
  154. },
  155. hasDate = function () {
  156. return (isEnabled('y') || isEnabled('M') || isEnabled('d'));
  157. },
  158. getDatePickerTemplate = function () {
  159. var headTemplate = $('<thead>')
  160. .append($('<tr>')
  161. .append($('<th>').addClass('prev').attr('data-action', 'previous')
  162. .append($('<span>').addClass(options.icons.previous))
  163. )
  164. .append($('<th>').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5')))
  165. .append($('<th>').addClass('next').attr('data-action', 'next')
  166. .append($('<span>').addClass(options.icons.next))
  167. )
  168. ),
  169. contTemplate = $('<tbody>')
  170. .append($('<tr>')
  171. .append($('<td>').attr('colspan', (options.calendarWeeks ? '8' : '7')))
  172. );
  173. return [
  174. $('<div>').addClass('datepicker-days')
  175. .append($('<table>').addClass('table-condensed')
  176. .append(headTemplate)
  177. .append($('<tbody>'))
  178. ),
  179. $('<div>').addClass('datepicker-months')
  180. .append($('<table>').addClass('table-condensed')
  181. .append(headTemplate.clone())
  182. .append(contTemplate.clone())
  183. ),
  184. $('<div>').addClass('datepicker-years')
  185. .append($('<table>').addClass('table-condensed')
  186. .append(headTemplate.clone())
  187. .append(contTemplate.clone())
  188. ),
  189. $('<div>').addClass('datepicker-decades')
  190. .append($('<table>').addClass('table-condensed')
  191. .append(headTemplate.clone())
  192. .append(contTemplate.clone())
  193. )
  194. ];
  195. },
  196. getTimePickerMainTemplate = function () {
  197. var topRow = $('<tr>'),
  198. middleRow = $('<tr>'),
  199. bottomRow = $('<tr>');
  200. if (isEnabled('h')) {
  201. topRow.append($('<td>')
  202. .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementHour }).addClass('btn').attr('data-action', 'incrementHours').append($('<span>').addClass(options.icons.up))));
  203. middleRow.append($('<td>')
  204. .append($('<span>').addClass('timepicker-hour').attr({ 'data-time-component': 'hours', 'title': options.tooltips.pickHour }).attr('data-action', 'showHours')));
  205. bottomRow.append($('<td>')
  206. .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementHour }).addClass('btn').attr('data-action', 'decrementHours').append($('<span>').addClass(options.icons.down))));
  207. }
  208. if (isEnabled('m')) {
  209. if (isEnabled('h')) {
  210. topRow.append($('<td>').addClass('separator'));
  211. middleRow.append($('<td>').addClass('separator').html(':'));
  212. bottomRow.append($('<td>').addClass('separator'));
  213. }
  214. topRow.append($('<td>')
  215. .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementMinute }).addClass('btn').attr('data-action', 'incrementMinutes')
  216. .append($('<span>').addClass(options.icons.up))));
  217. middleRow.append($('<td>')
  218. .append($('<span>').addClass('timepicker-minute').attr({ 'data-time-component': 'minutes', 'title': options.tooltips.pickMinute }).attr('data-action', 'showMinutes')));
  219. bottomRow.append($('<td>')
  220. .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementMinute }).addClass('btn').attr('data-action', 'decrementMinutes')
  221. .append($('<span>').addClass(options.icons.down))));
  222. }
  223. if (isEnabled('s')) {
  224. if (isEnabled('m')) {
  225. topRow.append($('<td>').addClass('separator'));
  226. middleRow.append($('<td>').addClass('separator').html(':'));
  227. bottomRow.append($('<td>').addClass('separator'));
  228. }
  229. topRow.append($('<td>')
  230. .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementSecond }).addClass('btn').attr('data-action', 'incrementSeconds')
  231. .append($('<span>').addClass(options.icons.up))));
  232. middleRow.append($('<td>')
  233. .append($('<span>').addClass('timepicker-second').attr({ 'data-time-component': 'seconds', 'title': options.tooltips.pickSecond }).attr('data-action', 'showSeconds')));
  234. bottomRow.append($('<td>')
  235. .append($('<a>').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementSecond }).addClass('btn').attr('data-action', 'decrementSeconds')
  236. .append($('<span>').addClass(options.icons.down))));
  237. }
  238. if (!use24Hours) {
  239. topRow.append($('<td>').addClass('separator'));
  240. middleRow.append($('<td>')
  241. .append($('<button>').addClass('btn btn-primary').attr({ 'data-action': 'togglePeriod', tabindex: '-1', 'title': options.tooltips.togglePeriod })));
  242. bottomRow.append($('<td>').addClass('separator'));
  243. }
  244. return $('<div>').addClass('timepicker-picker')
  245. .append($('<table>').addClass('table-condensed')
  246. .append([topRow, middleRow, bottomRow]));
  247. },
  248. getTimePickerTemplate = function () {
  249. var hoursView = $('<div>').addClass('timepicker-hours')
  250. .append($('<table>').addClass('table-condensed')),
  251. minutesView = $('<div>').addClass('timepicker-minutes')
  252. .append($('<table>').addClass('table-condensed')),
  253. secondsView = $('<div>').addClass('timepicker-seconds')
  254. .append($('<table>').addClass('table-condensed')),
  255. ret = [getTimePickerMainTemplate()];
  256. if (isEnabled('h')) {
  257. ret.push(hoursView);
  258. }
  259. if (isEnabled('m')) {
  260. ret.push(minutesView);
  261. }
  262. if (isEnabled('s')) {
  263. ret.push(secondsView);
  264. }
  265. return ret;
  266. },
  267. getToolbar = function () {
  268. var row = [];
  269. if (options.showTodayButton) {
  270. row.push($('<td>').append($('<a>').attr({ 'data-action': 'today', 'title': options.tooltips.today }).append($('<span>').addClass(options.icons.today))));
  271. }
  272. if (!options.sideBySide && hasDate() && hasTime()) {
  273. row.push($('<td>').append($('<a>').attr({ 'data-action': 'togglePicker', 'title': options.tooltips.selectTime }).append($('<span>').addClass(options.icons.time))));
  274. }
  275. if (options.showClear) {
  276. row.push($('<td>').append($('<a>').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('<span>').addClass(options.icons.clear))));
  277. }
  278. if (options.showClose) {
  279. row.push($('<td>').append($('<a>').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('<span>').addClass(options.icons.close))));
  280. }
  281. return $('<table>').addClass('table-condensed').append($('<tbody>').append($('<tr>').append(row)));
  282. },
  283. getTemplate = function () {
  284. var template = $('<div>').addClass('bootstrap-datetimepicker-widget dropdown-menu'),
  285. dateView = $('<div>').addClass('datepicker').append(getDatePickerTemplate()),
  286. timeView = $('<div>').addClass('timepicker').append(getTimePickerTemplate()),
  287. content = $('<ul>').addClass('list-unstyled'),
  288. toolbar = $('<li>').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar());
  289. if (options.inline) {
  290. template.removeClass('dropdown-menu');
  291. }
  292. if (use24Hours) {
  293. template.addClass('usetwentyfour');
  294. }
  295. if (isEnabled('s') && !use24Hours) {
  296. template.addClass('wider');
  297. }
  298. if (options.sideBySide && hasDate() && hasTime()) {
  299. template.addClass('timepicker-sbs');
  300. if (options.toolbarPlacement === 'top') {
  301. template.append(toolbar);
  302. }
  303. template.append(
  304. $('<div>').addClass('row')
  305. .append(dateView.addClass('col-md-6'))
  306. .append(timeView.addClass('col-md-6'))
  307. );
  308. if (options.toolbarPlacement === 'bottom') {
  309. template.append(toolbar);
  310. }
  311. return template;
  312. }
  313. if (options.toolbarPlacement === 'top') {
  314. content.append(toolbar);
  315. }
  316. if (hasDate()) {
  317. content.append($('<li>').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView));
  318. }
  319. if (options.toolbarPlacement === 'default') {
  320. content.append(toolbar);
  321. }
  322. if (hasTime()) {
  323. content.append($('<li>').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView));
  324. }
  325. if (options.toolbarPlacement === 'bottom') {
  326. content.append(toolbar);
  327. }
  328. return template.append(content);
  329. },
  330. dataToOptions = function () {
  331. var eData,
  332. dataOptions = {};
  333. if (element.is('input') || options.inline) {
  334. eData = element.data();
  335. } else {
  336. eData = element.find('input').data();
  337. }
  338. if (eData.dateOptions && eData.dateOptions instanceof Object) {
  339. dataOptions = $.extend(true, dataOptions, eData.dateOptions);
  340. }
  341. $.each(options, function (key) {
  342. var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1);
  343. if (eData[attributeName] !== undefined) {
  344. dataOptions[key] = eData[attributeName];
  345. }
  346. });
  347. return dataOptions;
  348. },
  349. place = function () {
  350. var position = (component || element).position(),
  351. offset = (component || element).offset(),
  352. vertical = options.widgetPositioning.vertical,
  353. horizontal = options.widgetPositioning.horizontal,
  354. parent;
  355. if (options.widgetParent) {
  356. parent = options.widgetParent.append(widget);
  357. } else if (element.is('input')) {
  358. parent = element.after(widget).parent();
  359. } else if (options.inline) {
  360. parent = element.append(widget);
  361. return;
  362. } else {
  363. parent = element;
  364. element.children().first().after(widget);
  365. }
  366. // Top and bottom logic
  367. if (vertical === 'auto') {
  368. if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() &&
  369. widget.height() + element.outerHeight() < offset.top) {
  370. vertical = 'top';
  371. } else {
  372. vertical = 'bottom';
  373. }
  374. }
  375. // Left and right logic
  376. if (horizontal === 'auto') {
  377. if (parent.width() < offset.left + widget.outerWidth() / 2 &&
  378. offset.left + widget.outerWidth() > $(window).width()) {
  379. horizontal = 'right';
  380. } else {
  381. horizontal = 'left';
  382. }
  383. }
  384. if (vertical === 'top') {
  385. widget.addClass('top').removeClass('bottom');
  386. } else {
  387. widget.addClass('bottom').removeClass('top');
  388. }
  389. if (horizontal === 'right') {
  390. widget.addClass('pull-right');
  391. } else {
  392. widget.removeClass('pull-right');
  393. }
  394. // find the first parent element that has a non-static css positioning
  395. if (parent.css('position') === 'static') {
  396. parent = parent.parents().filter(function () {
  397. return $(this).css('position') !== 'static';
  398. }).first();
  399. }
  400. if (parent.length === 0) {
  401. throw new Error('datetimepicker component should be placed within a non-static positioned container');
  402. }
  403. widget.css({
  404. top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(),
  405. bottom: vertical === 'top' ? parent.outerHeight() - (parent === element ? 0 : position.top) : 'auto',
  406. left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto',
  407. right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left)
  408. });
  409. },
  410. notifyEvent = function (e) {
  411. if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) {
  412. return;
  413. }
  414. element.trigger(e);
  415. },
  416. viewUpdate = function (e) {
  417. if (e === 'y') {
  418. e = 'YYYY';
  419. }
  420. notifyEvent({
  421. type: 'dp.update',
  422. change: e,
  423. viewDate: viewDate.clone()
  424. });
  425. },
  426. showMode = function (dir) {
  427. if (!widget) {
  428. return;
  429. }
  430. if (dir) {
  431. currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir));
  432. }
  433. widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show();
  434. },
  435. fillDow = function () {
  436. var row = $('<tr>'),
  437. currentDate = viewDate.clone().startOf('w').startOf('d');
  438. if (options.calendarWeeks === true) {
  439. row.append($('<th>').addClass('cw').text('#'));
  440. }
  441. while (currentDate.isBefore(viewDate.clone().endOf('w'))) {
  442. row.append($('<th>').addClass('dow').text(currentDate.format('dd')));
  443. currentDate.add(1, 'd');
  444. }
  445. widget.find('.datepicker-days thead').append(row);
  446. },
  447. isInDisabledDates = function (testDate) {
  448. return options.disabledDates[testDate.format('YYYY-MM-DD')] === true;
  449. },
  450. isInEnabledDates = function (testDate) {
  451. return options.enabledDates[testDate.format('YYYY-MM-DD')] === true;
  452. },
  453. isInDisabledHours = function (testDate) {
  454. return options.disabledHours[testDate.format('H')] === true;
  455. },
  456. isInEnabledHours = function (testDate) {
  457. return options.enabledHours[testDate.format('H')] === true;
  458. },
  459. isValid = function (targetMoment, granularity) {
  460. if (!targetMoment.isValid()) {
  461. return false;
  462. }
  463. if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) {
  464. return false;
  465. }
  466. if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) {
  467. return false;
  468. }
  469. if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) {
  470. return false;
  471. }
  472. if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) {
  473. return false;
  474. }
  475. if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) {
  476. return false;
  477. }
  478. if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) {
  479. return false;
  480. }
  481. if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) {
  482. return false;
  483. }
  484. if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) {
  485. var found = false;
  486. $.each(options.disabledTimeIntervals, function () {
  487. if (targetMoment.isBetween(this[0], this[1])) {
  488. found = true;
  489. return false;
  490. }
  491. });
  492. if (found) {
  493. return false;
  494. }
  495. }
  496. return true;
  497. },
  498. fillMonths = function () {
  499. var spans = [],
  500. monthsShort = viewDate.clone().startOf('y').startOf('d');
  501. while (monthsShort.isSame(viewDate, 'y')) {
  502. spans.push($('<span>').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM')));
  503. monthsShort.add(1, 'M');
  504. }
  505. widget.find('.datepicker-months td').empty().append(spans);
  506. },
  507. updateMonths = function () {
  508. var monthsView = widget.find('.datepicker-months'),
  509. monthsViewHeader = monthsView.find('th'),
  510. months = monthsView.find('tbody').find('span');
  511. monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear);
  512. monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear);
  513. monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear);
  514. monthsView.find('.disabled').removeClass('disabled');
  515. if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) {
  516. monthsViewHeader.eq(0).addClass('disabled');
  517. }
  518. monthsViewHeader.eq(1).text(viewDate.year());
  519. if (!isValid(viewDate.clone().add(1, 'y'), 'y')) {
  520. monthsViewHeader.eq(2).addClass('disabled');
  521. }
  522. months.removeClass('active');
  523. if (date.isSame(viewDate, 'y') && !unset) {
  524. months.eq(date.month()).addClass('active');
  525. }
  526. months.each(function (index) {
  527. if (!isValid(viewDate.clone().month(index), 'M')) {
  528. $(this).addClass('disabled');
  529. }
  530. });
  531. },
  532. updateYears = function () {
  533. var yearsView = widget.find('.datepicker-years'),
  534. yearsViewHeader = yearsView.find('th'),
  535. startYear = viewDate.clone().subtract(5, 'y'),
  536. endYear = viewDate.clone().add(6, 'y'),
  537. html = '';
  538. yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevDecade);
  539. yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade);
  540. yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextDecade);
  541. yearsView.find('.disabled').removeClass('disabled');
  542. if (options.minDate && options.minDate.isAfter(startYear, 'y')) {
  543. yearsViewHeader.eq(0).addClass('disabled');
  544. }
  545. yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year());
  546. if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) {
  547. yearsViewHeader.eq(2).addClass('disabled');
  548. }
  549. while (!startYear.isAfter(endYear, 'y')) {
  550. html += '<span data-action="selectYear" class="year' + (startYear.isSame(date, 'y') && !unset ? ' active' : '') + (!isValid(startYear, 'y') ? ' disabled' : '') + '">' + startYear.year() + '</span>';
  551. startYear.add(1, 'y');
  552. }
  553. yearsView.find('td').html(html);
  554. },
  555. updateDecades = function () {
  556. var decadesView = widget.find('.datepicker-decades'),
  557. decadesViewHeader = decadesView.find('th'),
  558. startDecade = moment({ y: viewDate.year() - (viewDate.year() % 100) - 1 }),
  559. endDecade = startDecade.clone().add(100, 'y'),
  560. startedAt = startDecade.clone(),
  561. minDateDecade = false,
  562. maxDateDecade = false,
  563. endDecadeYear,
  564. html = '';
  565. decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury);
  566. decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury);
  567. decadesView.find('.disabled').removeClass('disabled');
  568. if (startDecade.isSame(moment({ y: 1900 })) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) {
  569. decadesViewHeader.eq(0).addClass('disabled');
  570. }
  571. decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year());
  572. if (startDecade.isSame(moment({ y: 2000 })) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) {
  573. decadesViewHeader.eq(2).addClass('disabled');
  574. }
  575. while (!startDecade.isAfter(endDecade, 'y')) {
  576. endDecadeYear = startDecade.year() + 12;
  577. minDateDecade = options.minDate && options.minDate.isAfter(startDecade, 'y') && options.minDate.year() <= endDecadeYear;
  578. maxDateDecade = options.maxDate && options.maxDate.isAfter(startDecade, 'y') && options.maxDate.year() <= endDecadeYear;
  579. html += '<span data-action="selectDecade" class="decade' + (date.isAfter(startDecade) && date.year() <= endDecadeYear ? ' active' : '') +
  580. (!isValid(startDecade, 'y') && !minDateDecade && !maxDateDecade ? ' disabled' : '') + '" data-selection="' + (startDecade.year() + 6) + '">' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + '</span>';
  581. startDecade.add(12, 'y');
  582. }
  583. html += '<span></span><span></span><span></span>'; //push the dangling block over, at least this way it's even
  584. decadesView.find('td').html(html);
  585. decadesViewHeader.eq(1).text((startedAt.year() + 1) + '-' + (startDecade.year()));
  586. },
  587. fillDate = function () {
  588. var daysView = widget.find('.datepicker-days'),
  589. daysViewHeader = daysView.find('th'),
  590. currentDate,
  591. html = [],
  592. row,
  593. clsNames = [],
  594. i;
  595. if (!hasDate()) {
  596. return;
  597. }
  598. daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth);
  599. daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth);
  600. daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth);
  601. daysView.find('.disabled').removeClass('disabled');
  602. daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat));
  603. if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) {
  604. daysViewHeader.eq(0).addClass('disabled');
  605. }
  606. if (!isValid(viewDate.clone().add(1, 'M'), 'M')) {
  607. daysViewHeader.eq(2).addClass('disabled');
  608. }
  609. currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d');
  610. for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks)
  611. if (currentDate.weekday() === 0) {
  612. row = $('<tr>');
  613. if (options.calendarWeeks) {
  614. row.append('<td class="cw">' + currentDate.week() + '</td>');
  615. }
  616. html.push(row);
  617. }
  618. clsNames = ['day'];
  619. if (currentDate.isBefore(viewDate, 'M')) {
  620. clsNames.push('old');
  621. }
  622. if (currentDate.isAfter(viewDate, 'M')) {
  623. clsNames.push('new');
  624. }
  625. if (currentDate.isSame(date, 'd') && !unset) {
  626. clsNames.push('active');
  627. }
  628. if (!isValid(currentDate, 'd')) {
  629. clsNames.push('disabled');
  630. }
  631. if (currentDate.isSame(getMoment(), 'd')) {
  632. clsNames.push('today');
  633. }
  634. if (currentDate.day() === 0 || currentDate.day() === 6) {
  635. clsNames.push('weekend');
  636. }
  637. notifyEvent({
  638. type: 'dp.classify',
  639. date: currentDate,
  640. classNames: clsNames
  641. });
  642. row.append('<td data-action="selectDay" data-day="' + currentDate.format('L') + '" class="' + clsNames.join(' ') + '">' + currentDate.date() + '</td>');
  643. currentDate.add(1, 'd');
  644. }
  645. daysView.find('tbody').empty().append(html);
  646. updateMonths();
  647. updateYears();
  648. updateDecades();
  649. },
  650. fillHours = function () {
  651. var table = widget.find('.timepicker-hours table'),
  652. currentHour = viewDate.clone().startOf('d'),
  653. html = [],
  654. row = $('<tr>');
  655. if (viewDate.hour() > 11 && !use24Hours) {
  656. currentHour.hour(12);
  657. }
  658. while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) {
  659. if (currentHour.hour() % 4 === 0) {
  660. row = $('<tr>');
  661. html.push(row);
  662. }
  663. row.append('<td data-action="selectHour" class="hour' + (!isValid(currentHour, 'h') ? ' disabled' : '') + '">' + currentHour.format(use24Hours ? 'HH' : 'hh') + '</td>');
  664. currentHour.add(1, 'h');
  665. }
  666. table.empty().append(html);
  667. },
  668. fillMinutes = function () {
  669. var table = widget.find('.timepicker-minutes table'),
  670. currentMinute = viewDate.clone().startOf('h'),
  671. html = [],
  672. row = $('<tr>'),
  673. step = options.stepping === 1 ? 5 : options.stepping;
  674. while (viewDate.isSame(currentMinute, 'h')) {
  675. if (currentMinute.minute() % (step * 4) === 0) {
  676. row = $('<tr>');
  677. html.push(row);
  678. }
  679. row.append('<td data-action="selectMinute" class="minute' + (!isValid(currentMinute, 'm') ? ' disabled' : '') + '">' + currentMinute.format('mm') + '</td>');
  680. currentMinute.add(step, 'm');
  681. }
  682. table.empty().append(html);
  683. },
  684. fillSeconds = function () {
  685. var table = widget.find('.timepicker-seconds table'),
  686. currentSecond = viewDate.clone().startOf('m'),
  687. html = [],
  688. row = $('<tr>');
  689. while (viewDate.isSame(currentSecond, 'm')) {
  690. if (currentSecond.second() % 20 === 0) {
  691. row = $('<tr>');
  692. html.push(row);
  693. }
  694. row.append('<td data-action="selectSecond" class="second' + (!isValid(currentSecond, 's') ? ' disabled' : '') + '">' + currentSecond.format('ss') + '</td>');
  695. currentSecond.add(5, 's');
  696. }
  697. table.empty().append(html);
  698. },
  699. fillTime = function () {
  700. var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]');
  701. if (!use24Hours) {
  702. toggle = widget.find('.timepicker [data-action=togglePeriod]');
  703. newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h');
  704. toggle.text(date.format('A'));
  705. if (isValid(newDate, 'h')) {
  706. toggle.removeClass('disabled');
  707. } else {
  708. toggle.addClass('disabled');
  709. }
  710. }
  711. timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh'));
  712. timeComponents.filter('[data-time-component=minutes]').text(date.format('mm'));
  713. timeComponents.filter('[data-time-component=seconds]').text(date.format('ss'));
  714. fillHours();
  715. fillMinutes();
  716. fillSeconds();
  717. },
  718. update = function () {
  719. if (!widget) {
  720. return;
  721. }
  722. fillDate();
  723. fillTime();
  724. },
  725. setValue = function (targetMoment) {
  726. var oldDate = unset ? null : date;
  727. // case of calling setValue(null or false)
  728. if (!targetMoment) {
  729. unset = true;
  730. input.val('');
  731. element.data('date', '');
  732. notifyEvent({
  733. type: 'dp.change',
  734. date: false,
  735. oldDate: oldDate
  736. });
  737. update();
  738. return;
  739. }
  740. targetMoment = targetMoment.clone().locale(options.locale);
  741. if (hasTimeZone()) {
  742. targetMoment.tz(options.timeZone);
  743. }
  744. if (options.stepping !== 1) {
  745. targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping)).seconds(0);
  746. while (options.minDate && targetMoment.isBefore(options.minDate)) {
  747. targetMoment.add(options.stepping, 'minutes');
  748. }
  749. }
  750. if (isValid(targetMoment)) {
  751. date = targetMoment;
  752. viewDate = date.clone();
  753. input.val(date.format(actualFormat));
  754. element.data('date', date.format(actualFormat));
  755. unset = false;
  756. update();
  757. notifyEvent({
  758. type: 'dp.change',
  759. date: date.clone(),
  760. oldDate: oldDate
  761. });
  762. } else {
  763. if (!options.keepInvalid) {
  764. input.val(unset ? '' : date.format(actualFormat));
  765. } else {
  766. notifyEvent({
  767. type: 'dp.change',
  768. date: targetMoment,
  769. oldDate: oldDate
  770. });
  771. }
  772. notifyEvent({
  773. type: 'dp.error',
  774. date: targetMoment,
  775. oldDate: oldDate
  776. });
  777. }
  778. },
  779. /**
  780. * Hides the widget. Possibly will emit dp.hide
  781. */
  782. hide = function () {
  783. var transitioning = false;
  784. if (!widget) {
  785. return picker;
  786. }
  787. // Ignore event if in the middle of a picker transition
  788. widget.find('.collapse').each(function () {
  789. var collapseData = $(this).data('collapse');
  790. if (collapseData && collapseData.transitioning) {
  791. transitioning = true;
  792. return false;
  793. }
  794. return true;
  795. });
  796. if (transitioning) {
  797. return picker;
  798. }
  799. if (component && component.hasClass('btn')) {
  800. component.toggleClass('active');
  801. }
  802. widget.hide();
  803. $(window).off('resize', place);
  804. widget.off('click', '[data-action]');
  805. widget.off('mousedown', false);
  806. widget.remove();
  807. widget = false;
  808. notifyEvent({
  809. type: 'dp.hide',
  810. date: date.clone()
  811. });
  812. input.blur();
  813. viewDate = date.clone();
  814. return picker;
  815. },
  816. clear = function () {
  817. setValue(null);
  818. },
  819. parseInputDate = function (inputDate) {
  820. if (options.parseInputDate === undefined) {
  821. if (!moment.isMoment(inputDate) || inputDate instanceof Date) {
  822. inputDate = getMoment(inputDate);
  823. }
  824. } else {
  825. inputDate = options.parseInputDate(inputDate);
  826. }
  827. //inputDate.locale(options.locale);
  828. return inputDate;
  829. },
  830. /********************************************************************************
  831. *
  832. * Widget UI interaction functions
  833. *
  834. ********************************************************************************/
  835. actions = {
  836. next: function () {
  837. var navFnc = datePickerModes[currentViewMode].navFnc;
  838. viewDate.add(datePickerModes[currentViewMode].navStep, navFnc);
  839. fillDate();
  840. viewUpdate(navFnc);
  841. },
  842. previous: function () {
  843. var navFnc = datePickerModes[currentViewMode].navFnc;
  844. viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc);
  845. fillDate();
  846. viewUpdate(navFnc);
  847. },
  848. pickerSwitch: function () {
  849. showMode(1);
  850. },
  851. selectMonth: function (e) {
  852. var month = $(e.target).closest('tbody').find('span').index($(e.target));
  853. viewDate.month(month);
  854. if (currentViewMode === minViewModeNumber) {
  855. setValue(date.clone().year(viewDate.year()).month(viewDate.month()));
  856. if (!options.inline) {
  857. hide();
  858. }
  859. } else {
  860. showMode(-1);
  861. fillDate();
  862. }
  863. viewUpdate('M');
  864. },
  865. selectYear: function (e) {
  866. var year = parseInt($(e.target).text(), 10) || 0;
  867. viewDate.year(year);
  868. if (currentViewMode === minViewModeNumber) {
  869. setValue(date.clone().year(viewDate.year()));
  870. if (!options.inline) {
  871. hide();
  872. }
  873. } else {
  874. showMode(-1);
  875. fillDate();
  876. }
  877. viewUpdate('YYYY');
  878. },
  879. selectDecade: function (e) {
  880. var year = parseInt($(e.target).data('selection'), 10) || 0;
  881. viewDate.year(year);
  882. if (currentViewMode === minViewModeNumber) {
  883. setValue(date.clone().year(viewDate.year()));
  884. if (!options.inline) {
  885. hide();
  886. }
  887. } else {
  888. showMode(-1);
  889. fillDate();
  890. }
  891. viewUpdate('YYYY');
  892. },
  893. selectDay: function (e) {
  894. var day = viewDate.clone();
  895. if ($(e.target).is('.old')) {
  896. day.subtract(1, 'M');
  897. }
  898. if ($(e.target).is('.new')) {
  899. day.add(1, 'M');
  900. }
  901. setValue(day.date(parseInt($(e.target).text(), 10)));
  902. if (!hasTime() && !options.keepOpen && !options.inline) {
  903. hide();
  904. }
  905. },
  906. incrementHours: function () {
  907. var newDate = date.clone().add(1, 'h');
  908. if (isValid(newDate, 'h')) {
  909. setValue(newDate);
  910. }
  911. },
  912. incrementMinutes: function () {
  913. var newDate = date.clone().add(options.stepping, 'm');
  914. if (isValid(newDate, 'm')) {
  915. setValue(newDate);
  916. }
  917. },
  918. incrementSeconds: function () {
  919. var newDate = date.clone().add(1, 's');
  920. if (isValid(newDate, 's')) {
  921. setValue(newDate);
  922. }
  923. },
  924. decrementHours: function () {
  925. var newDate = date.clone().subtract(1, 'h');
  926. if (isValid(newDate, 'h')) {
  927. setValue(newDate);
  928. }
  929. },
  930. decrementMinutes: function () {
  931. var newDate = date.clone().subtract(options.stepping, 'm');
  932. if (isValid(newDate, 'm')) {
  933. setValue(newDate);
  934. }
  935. },
  936. decrementSeconds: function () {
  937. var newDate = date.clone().subtract(1, 's');
  938. if (isValid(newDate, 's')) {
  939. setValue(newDate);
  940. }
  941. },
  942. togglePeriod: function () {
  943. setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'));
  944. },
  945. togglePicker: function (e) {
  946. var $this = $(e.target),
  947. $parent = $this.closest('ul'),
  948. expanded = $parent.find('.in'),
  949. closed = $parent.find('.collapse:not(.in)'),
  950. collapseData;
  951. if (expanded && expanded.length) {
  952. collapseData = expanded.data('collapse');
  953. if (collapseData && collapseData.transitioning) {
  954. return;
  955. }
  956. if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it
  957. expanded.collapse('hide');
  958. closed.collapse('show');
  959. } else { // otherwise just toggle in class on the two views
  960. expanded.removeClass('in');
  961. closed.addClass('in');
  962. }
  963. if ($this.is('span')) {
  964. $this.toggleClass(options.icons.time + ' ' + options.icons.date);
  965. } else {
  966. $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
  967. }
  968. // NOTE: uncomment if toggled state will be restored in show()
  969. //if (component) {
  970. // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
  971. //}
  972. }
  973. },
  974. showPicker: function () {
  975. widget.find('.timepicker > div:not(.timepicker-picker)').hide();
  976. widget.find('.timepicker .timepicker-picker').show();
  977. },
  978. showHours: function () {
  979. widget.find('.timepicker .timepicker-picker').hide();
  980. widget.find('.timepicker .timepicker-hours').show();
  981. },
  982. showMinutes: function () {
  983. widget.find('.timepicker .timepicker-picker').hide();
  984. widget.find('.timepicker .timepicker-minutes').show();
  985. },
  986. showSeconds: function () {
  987. widget.find('.timepicker .timepicker-picker').hide();
  988. widget.find('.timepicker .timepicker-seconds').show();
  989. },
  990. selectHour: function (e) {
  991. var hour = parseInt($(e.target).text(), 10);
  992. if (!use24Hours) {
  993. if (date.hours() >= 12) {
  994. if (hour !== 12) {
  995. hour += 12;
  996. }
  997. } else {
  998. if (hour === 12) {
  999. hour = 0;
  1000. }
  1001. }
  1002. }
  1003. setValue(date.clone().hours(hour));
  1004. actions.showPicker.call(picker);
  1005. },
  1006. selectMinute: function (e) {
  1007. setValue(date.clone().minutes(parseInt($(e.target).text(), 10)));
  1008. actions.showPicker.call(picker);
  1009. },
  1010. selectSecond: function (e) {
  1011. setValue(date.clone().seconds(parseInt($(e.target).text(), 10)));
  1012. actions.showPicker.call(picker);
  1013. },
  1014. clear: clear,
  1015. today: function () {
  1016. var todaysDate = getMoment();
  1017. if (isValid(todaysDate, 'd')) {
  1018. setValue(todaysDate);
  1019. }
  1020. },
  1021. close: hide
  1022. },
  1023. doAction = function (e) {
  1024. if ($(e.currentTarget).is('.disabled')) {
  1025. return false;
  1026. }
  1027. actions[$(e.currentTarget).data('action')].apply(picker, arguments);
  1028. return false;
  1029. },
  1030. /**
  1031. * Shows the widget. Possibly will emit dp.show and dp.change
  1032. */
  1033. show = function () {
  1034. var currentMoment,
  1035. useCurrentGranularity = {
  1036. 'year': function (m) {
  1037. return m.month(0).date(1).hours(0).seconds(0).minutes(0);
  1038. },
  1039. 'month': function (m) {
  1040. return m.date(1).hours(0).seconds(0).minutes(0);
  1041. },
  1042. 'day': function (m) {
  1043. return m.hours(0).seconds(0).minutes(0);
  1044. },
  1045. 'hour': function (m) {
  1046. return m.seconds(0).minutes(0);
  1047. },
  1048. 'minute': function (m) {
  1049. return m.seconds(0);
  1050. }
  1051. };
  1052. if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) {
  1053. return picker;
  1054. }
  1055. if (input.val() !== undefined && input.val().trim().length !== 0) {
  1056. setValue(parseInputDate(input.val().trim()));
  1057. } else if (unset && options.useCurrent && (options.inline || (input.is('input') && input.val().trim().length === 0))) {
  1058. currentMoment = getMoment();
  1059. if (typeof options.useCurrent === 'string') {
  1060. currentMoment = useCurrentGranularity[options.useCurrent](currentMoment);
  1061. }
  1062. setValue(currentMoment);
  1063. }
  1064. widget = getTemplate();
  1065. fillDow();
  1066. fillMonths();
  1067. widget.find('.timepicker-hours').hide();
  1068. widget.find('.timepicker-minutes').hide();
  1069. widget.find('.timepicker-seconds').hide();
  1070. update();
  1071. showMode();
  1072. $(window).on('resize', place);
  1073. widget.on('click', '[data-action]', doAction); // this handles clicks on the widget
  1074. widget.on('mousedown', false);
  1075. if (component && component.hasClass('btn')) {
  1076. component.toggleClass('active');
  1077. }
  1078. place();
  1079. widget.show();
  1080. if (options.focusOnShow && !input.is(':focus')) {
  1081. input.focus();
  1082. }
  1083. notifyEvent({
  1084. type: 'dp.show'
  1085. });
  1086. return picker;
  1087. },
  1088. /**
  1089. * Shows or hides the widget
  1090. */
  1091. toggle = function () {
  1092. return (widget ? hide() : show());
  1093. },
  1094. keydown = function (e) {
  1095. var handler = null,
  1096. index,
  1097. index2,
  1098. pressedKeys = [],
  1099. pressedModifiers = {},
  1100. currentKey = e.which,
  1101. keyBindKeys,
  1102. allModifiersPressed,
  1103. pressed = 'p';
  1104. keyState[currentKey] = pressed;
  1105. for (index in keyState) {
  1106. if (keyState.hasOwnProperty(index) && keyState[index] === pressed) {
  1107. pressedKeys.push(index);
  1108. if (parseInt(index, 10) !== currentKey) {
  1109. pressedModifiers[index] = true;
  1110. }
  1111. }
  1112. }
  1113. for (index in options.keyBinds) {
  1114. if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') {
  1115. keyBindKeys = index.split(' ');
  1116. if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) {
  1117. allModifiersPressed = true;
  1118. for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) {
  1119. if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) {
  1120. allModifiersPressed = false;
  1121. break;
  1122. }
  1123. }
  1124. if (allModifiersPressed) {
  1125. handler = options.keyBinds[index];
  1126. break;
  1127. }
  1128. }
  1129. }
  1130. }
  1131. if (handler) {
  1132. handler.call(picker, widget);
  1133. e.stopPropagation();
  1134. e.preventDefault();
  1135. }
  1136. },
  1137. keyup = function (e) {
  1138. keyState[e.which] = 'r';
  1139. e.stopPropagation();
  1140. e.preventDefault();
  1141. },
  1142. change = function (e) {
  1143. var val = $(e.target).val().trim(),
  1144. parsedDate = val ? parseInputDate(val) : null;
  1145. setValue(parsedDate);
  1146. e.stopImmediatePropagation();
  1147. return false;
  1148. },
  1149. attachDatePickerElementEvents = function () {
  1150. input.on({
  1151. 'change': change,
  1152. 'blur': options.debug ? '' : hide,
  1153. 'keydown': keydown,
  1154. 'keyup': keyup,
  1155. 'focus': options.allowInputToggle ? show : ''
  1156. });
  1157. if (element.is('input')) {
  1158. input.on({
  1159. 'focus': show
  1160. });
  1161. } else if (component) {
  1162. component.on('click', toggle);
  1163. component.on('mousedown', false);
  1164. }
  1165. },
  1166. detachDatePickerElementEvents = function () {
  1167. input.off({
  1168. 'change': change,
  1169. 'blur': blur,
  1170. 'keydown': keydown,
  1171. 'keyup': keyup,
  1172. 'focus': options.allowInputToggle ? hide : ''
  1173. });
  1174. if (element.is('input')) {
  1175. input.off({
  1176. 'focus': show
  1177. });
  1178. } else if (component) {
  1179. component.off('click', toggle);
  1180. component.off('mousedown', false);
  1181. }
  1182. },
  1183. indexGivenDates = function (givenDatesArray) {
  1184. // Store given enabledDates and disabledDates as keys.
  1185. // This way we can check their existence in O(1) time instead of looping through whole array.
  1186. // (for example: options.enabledDates['2014-02-27'] === true)
  1187. var givenDatesIndexed = {};
  1188. $.each(givenDatesArray, function () {
  1189. var dDate = parseInputDate(this);
  1190. if (dDate.isValid()) {
  1191. givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true;
  1192. }
  1193. });
  1194. return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false;
  1195. },
  1196. indexGivenHours = function (givenHoursArray) {
  1197. // Store given enabledHours and disabledHours as keys.
  1198. // This way we can check their existence in O(1) time instead of looping through whole array.
  1199. // (for example: options.enabledHours['2014-02-27'] === true)
  1200. var givenHoursIndexed = {};
  1201. $.each(givenHoursArray, function () {
  1202. givenHoursIndexed[this] = true;
  1203. });
  1204. return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false;
  1205. },
  1206. initFormatting = function () {
  1207. var format = options.format || 'L LT';
  1208. actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) {
  1209. var newinput = date.localeData().longDateFormat(formatInput) || formatInput;
  1210. return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740
  1211. return date.localeData().longDateFormat(formatInput2) || formatInput2;
  1212. });
  1213. });
  1214. parseFormats = options.extraFormats ? options.extraFormats.slice() : [];
  1215. if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) {
  1216. parseFormats.push(actualFormat);
  1217. }
  1218. use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1);
  1219. if (isEnabled('y')) {
  1220. minViewModeNumber = 2;
  1221. }
  1222. if (isEnabled('M')) {
  1223. minViewModeNumber = 1;
  1224. }
  1225. if (isEnabled('d')) {
  1226. minViewModeNumber = 0;
  1227. }
  1228. currentViewMode = Math.max(minViewModeNumber, currentViewMode);
  1229. if (!unset) {
  1230. setValue(date);
  1231. }
  1232. };
  1233. /********************************************************************************
  1234. *
  1235. * Public API functions
  1236. * =====================
  1237. *
  1238. * Important: Do not expose direct references to private objects or the options
  1239. * object to the outer world. Always return a clone when returning values or make
  1240. * a clone when setting a private variable.
  1241. *
  1242. ********************************************************************************/
  1243. picker.destroy = function () {
  1244. ///<summary>Destroys the widget and removes all attached event listeners</summary>
  1245. hide();
  1246. detachDatePickerElementEvents();
  1247. element.removeData('DateTimePicker');
  1248. element.removeData('date');
  1249. };
  1250. picker.toggle = toggle;
  1251. picker.show = show;
  1252. picker.hide = hide;
  1253. picker.disable = function () {
  1254. ///<summary>Disables the input element, the component is attached to, by adding a disabled="true" attribute to it.
  1255. ///If the widget was visible before that call it is hidden. Possibly emits dp.hide</summary>
  1256. hide();
  1257. if (component && component.hasClass('btn')) {
  1258. component.addClass('disabled');
  1259. }
  1260. input.prop('disabled', true);
  1261. return picker;
  1262. };
  1263. picker.enable = function () {
  1264. ///<summary>Enables the input element, the component is attached to, by removing disabled attribute from it.</summary>
  1265. if (component && component.hasClass('btn')) {
  1266. component.removeClass('disabled');
  1267. }
  1268. input.prop('disabled', false);
  1269. return picker;
  1270. };
  1271. picker.ignoreReadonly = function (ignoreReadonly) {
  1272. if (arguments.length === 0) {
  1273. return options.ignoreReadonly;
  1274. }
  1275. if (typeof ignoreReadonly !== 'boolean') {
  1276. throw new TypeError('ignoreReadonly () expects a boolean parameter');
  1277. }
  1278. options.ignoreReadonly = ignoreReadonly;
  1279. return picker;
  1280. };
  1281. picker.options = function (newOptions) {
  1282. if (arguments.length === 0) {
  1283. return $.extend(true, {}, options);
  1284. }
  1285. if (!(newOptions instanceof Object)) {
  1286. throw new TypeError('options() options parameter should be an object');
  1287. }
  1288. $.extend(true, options, newOptions);
  1289. $.each(options, function (key, value) {
  1290. if (picker[key] !== undefined) {
  1291. picker[key](value);
  1292. } else {
  1293. throw new TypeError('option ' + key + ' is not recognized!');
  1294. }
  1295. });
  1296. return picker;
  1297. };
  1298. picker.date = function (newDate) {
  1299. ///<signature helpKeyword="$.fn.datetimepicker.date">
  1300. ///<summary>Returns the component's model current date, a moment object or null if not set.</summary>
  1301. ///<returns type="Moment">date.clone()</returns>
  1302. ///</signature>
  1303. ///<signature>
  1304. ///<summary>Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.</summary>
  1305. ///<param name="newDate" locid="$.fn.datetimepicker.date_p:newDate">Takes string, Date, moment, null parameter.</param>
  1306. ///</signature>
  1307. if (arguments.length === 0) {
  1308. if (unset) {
  1309. return null;
  1310. }
  1311. return date.clone();
  1312. }
  1313. if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
  1314. throw new TypeError('date() parameter must be one of [null, string, moment or Date]');
  1315. }
  1316. setValue(newDate === null ? null : parseInputDate(newDate));
  1317. return picker;
  1318. };
  1319. picker.format = function (newFormat) {
  1320. ///<summary>test su</summary>
  1321. ///<param name="newFormat">info about para</param>
  1322. ///<returns type="string|boolean">returns foo</returns>
  1323. if (arguments.length === 0) {
  1324. return options.format;
  1325. }
  1326. if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) {
  1327. throw new TypeError('format() expects a string or boolean:false parameter ' + newFormat);
  1328. }
  1329. options.format = newFormat;
  1330. if (actualFormat) {
  1331. initFormatting(); // reinit formatting
  1332. }
  1333. return picker;
  1334. };
  1335. picker.timeZone = function (newZone) {
  1336. if (arguments.length === 0) {
  1337. return options.timeZone;
  1338. }
  1339. if (typeof newZone !== 'string') {
  1340. throw new TypeError('newZone() expects a string parameter');
  1341. }
  1342. options.timeZone = newZone;
  1343. return picker;
  1344. };
  1345. picker.dayViewHeaderFormat = function (newFormat) {
  1346. if (arguments.length === 0) {
  1347. return options.dayViewHeaderFormat;
  1348. }
  1349. if (typeof newFormat !== 'string') {
  1350. throw new TypeError('dayViewHeaderFormat() expects a string parameter');
  1351. }
  1352. options.dayViewHeaderFormat = newFormat;
  1353. return picker;
  1354. };
  1355. picker.extraFormats = function (formats) {
  1356. if (arguments.length === 0) {
  1357. return options.extraFormats;
  1358. }
  1359. if (formats !== false && !(formats instanceof Array)) {
  1360. throw new TypeError('extraFormats() expects an array or false parameter');
  1361. }
  1362. options.extraFormats = formats;
  1363. if (parseFormats) {
  1364. initFormatting(); // reinit formatting
  1365. }
  1366. return picker;
  1367. };
  1368. picker.disabledDates = function (dates) {
  1369. ///<signature helpKeyword="$.fn.datetimepicker.disabledDates">
  1370. ///<summary>Returns an array with the currently set disabled dates on the component.</summary>
  1371. ///<returns type="array">options.disabledDates</returns>
  1372. ///</signature>
  1373. ///<signature>
  1374. ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
  1375. ///options.enabledDates if such exist.</summary>
  1376. ///<param name="dates" locid="$.fn.datetimepicker.disabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
  1377. ///</signature>
  1378. if (arguments.length === 0) {
  1379. return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates);
  1380. }
  1381. if (!dates) {
  1382. options.disabledDates = false;
  1383. update();
  1384. return picker;
  1385. }
  1386. if (!(dates instanceof Array)) {
  1387. throw new TypeError('disabledDates() expects an array parameter');
  1388. }
  1389. options.disabledDates = indexGivenDates(dates);
  1390. options.enabledDates = false;
  1391. update();
  1392. return picker;
  1393. };
  1394. picker.enabledDates = function (dates) {
  1395. ///<signature helpKeyword="$.fn.datetimepicker.enabledDates">
  1396. ///<summary>Returns an array with the currently set enabled dates on the component.</summary>
  1397. ///<returns type="array">options.enabledDates</returns>
  1398. ///</signature>
  1399. ///<signature>
  1400. ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist.</summary>
  1401. ///<param name="dates" locid="$.fn.datetimepicker.enabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
  1402. ///</signature>
  1403. if (arguments.length === 0) {
  1404. return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates);
  1405. }
  1406. if (!dates) {
  1407. options.enabledDates = false;
  1408. update();
  1409. return picker;
  1410. }
  1411. if (!(dates instanceof Array)) {
  1412. throw new TypeError('enabledDates() expects an array parameter');
  1413. }
  1414. options.enabledDates = indexGivenDates(dates);
  1415. options.disabledDates = false;
  1416. update();
  1417. return picker;
  1418. };
  1419. picker.daysOfWeekDisabled = function (daysOfWeekDisabled) {
  1420. if (arguments.length === 0) {
  1421. return options.daysOfWeekDisabled.splice(0);
  1422. }
  1423. if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) {
  1424. options.daysOfWeekDisabled = false;
  1425. update();
  1426. return picker;
  1427. }
  1428. if (!(daysOfWeekDisabled instanceof Array)) {
  1429. throw new TypeError('daysOfWeekDisabled() expects an array parameter');
  1430. }
  1431. options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) {
  1432. currentValue = parseInt(currentValue, 10);
  1433. if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) {
  1434. return previousValue;
  1435. }
  1436. if (previousValue.indexOf(currentValue) === -1) {
  1437. previousValue.push(currentValue);
  1438. }
  1439. return previousValue;
  1440. }, []).sort();
  1441. if (options.useCurrent && !options.keepInvalid) {
  1442. var tries = 0;
  1443. while (!isValid(date, 'd')) {
  1444. date.add(1, 'd');
  1445. if (tries === 31) {
  1446. throw 'Tried 31 times to find a valid date';
  1447. }
  1448. tries++;
  1449. }
  1450. setValue(date);
  1451. }
  1452. update();
  1453. return picker;
  1454. };
  1455. picker.maxDate = function (maxDate) {
  1456. if (arguments.length === 0) {
  1457. return options.maxDate ? options.maxDate.clone() : options.maxDate;
  1458. }
  1459. if ((typeof maxDate === 'boolean') && maxDate === false) {
  1460. options.maxDate = false;
  1461. update();
  1462. return picker;
  1463. }
  1464. if (typeof maxDate === 'string') {
  1465. if (maxDate === 'now' || maxDate === 'moment') {
  1466. maxDate = getMoment();
  1467. }
  1468. }
  1469. var parsedDate = parseInputDate(maxDate);
  1470. if (!parsedDate.isValid()) {
  1471. throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate);
  1472. }
  1473. if (options.minDate && parsedDate.isBefore(options.minDate)) {
  1474. throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat));
  1475. }
  1476. options.maxDate = parsedDate;
  1477. if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) {
  1478. setValue(options.maxDate);
  1479. }
  1480. if (viewDate.isAfter(parsedDate)) {
  1481. viewDate = parsedDate.clone().subtract(options.stepping, 'm');
  1482. }
  1483. update();
  1484. return picker;
  1485. };
  1486. picker.minDate = function (minDate) {
  1487. if (arguments.length === 0) {
  1488. return options.minDate ? options.minDate.clone() : options.minDate;
  1489. }
  1490. if ((typeof minDate === 'boolean') && minDate === false) {
  1491. options.minDate = false;
  1492. update();
  1493. return picker;
  1494. }
  1495. if (typeof minDate === 'string') {
  1496. if (minDate === 'now' || minDate === 'moment') {
  1497. minDate = getMoment();
  1498. }
  1499. }
  1500. var parsedDate = parseInputDate(minDate);
  1501. if (!parsedDate.isValid()) {
  1502. throw new TypeError('minDate() Could not parse date parameter: ' + minDate);
  1503. }
  1504. if (options.maxDate && parsedDate.isAfter(options.maxDate)) {
  1505. throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat));
  1506. }
  1507. options.minDate = parsedDate;
  1508. if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) {
  1509. setValue(options.minDate);
  1510. }
  1511. if (viewDate.isBefore(parsedDate)) {
  1512. viewDate = parsedDate.clone().add(options.stepping, 'm');
  1513. }
  1514. update();
  1515. return picker;
  1516. };
  1517. picker.defaultDate = function (defaultDate) {
  1518. ///<signature helpKeyword="$.fn.datetimepicker.defaultDate">
  1519. ///<summary>Returns a moment with the options.defaultDate option configuration or false if not set</summary>
  1520. ///<returns type="Moment">date.clone()</returns>
  1521. ///</signature>
  1522. ///<signature>
  1523. ///<summary>Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared.</summary>
  1524. ///<param name="defaultDate" locid="$.fn.datetimepicker.defaultDate_p:defaultDate">Takes a string, Date, moment, boolean:false</param>
  1525. ///</signature>
  1526. if (arguments.length === 0) {
  1527. return options.defaultDate ? options.defaultDate.clone() : options.defaultDate;
  1528. }
  1529. if (!defaultDate) {
  1530. options.defaultDate = false;
  1531. return picker;
  1532. }
  1533. if (typeof defaultDate === 'string') {
  1534. if (defaultDate === 'now' || defaultDate === 'moment') {
  1535. defaultDate = getMoment();
  1536. } else {
  1537. defaultDate = getMoment(defaultDate);
  1538. }
  1539. }
  1540. var parsedDate = parseInputDate(defaultDate);
  1541. if (!parsedDate.isValid()) {
  1542. throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate);
  1543. }
  1544. if (!isValid(parsedDate)) {
  1545. throw new TypeError('defaultDate() date passed is invalid according to component setup validations');
  1546. }
  1547. options.defaultDate = parsedDate;
  1548. if ((options.defaultDate && options.inline) || input.val().trim() === '') {
  1549. setValue(options.defaultDate);
  1550. }
  1551. return picker;
  1552. };
  1553. picker.locale = function (locale) {
  1554. if (arguments.length === 0) {
  1555. return options.locale;
  1556. }
  1557. if (!moment.localeData(locale)) {
  1558. throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!');
  1559. }
  1560. options.locale = locale;
  1561. date.locale(options.locale);
  1562. viewDate.locale(options.locale);
  1563. if (actualFormat) {
  1564. initFormatting(); // reinit formatting
  1565. }
  1566. if (widget) {
  1567. hide();
  1568. show();
  1569. }
  1570. return picker;
  1571. };
  1572. picker.stepping = function (stepping) {
  1573. if (arguments.length === 0) {
  1574. return options.stepping;
  1575. }
  1576. stepping = parseInt(stepping, 10);
  1577. if (isNaN(stepping) || stepping < 1) {
  1578. stepping = 1;
  1579. }
  1580. options.stepping = stepping;
  1581. return picker;
  1582. };
  1583. picker.useCurrent = function (useCurrent) {
  1584. var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute'];
  1585. if (arguments.length === 0) {
  1586. return options.useCurrent;
  1587. }
  1588. if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) {
  1589. throw new TypeError('useCurrent() expects a boolean or string parameter');
  1590. }
  1591. if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) {
  1592. throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', '));
  1593. }
  1594. options.useCurrent = useCurrent;
  1595. return picker;
  1596. };
  1597. picker.collapse = function (collapse) {
  1598. if (arguments.length === 0) {
  1599. return options.collapse;
  1600. }
  1601. if (typeof collapse !== 'boolean') {
  1602. throw new TypeError('collapse() expects a boolean parameter');
  1603. }
  1604. if (options.collapse === collapse) {
  1605. return picker;
  1606. }
  1607. options.collapse = collapse;
  1608. if (widget) {
  1609. hide();
  1610. show();
  1611. }
  1612. return picker;
  1613. };
  1614. picker.icons = function (icons) {
  1615. if (arguments.length === 0) {
  1616. return $.extend({}, options.icons);
  1617. }
  1618. if (!(icons instanceof Object)) {
  1619. throw new TypeError('icons() expects parameter to be an Object');
  1620. }
  1621. $.extend(options.icons, icons);
  1622. if (widget) {
  1623. hide();
  1624. show();
  1625. }
  1626. return picker;
  1627. };
  1628. picker.tooltips = function (tooltips) {
  1629. if (arguments.length === 0) {
  1630. return $.extend({}, options.tooltips);
  1631. }
  1632. if (!(tooltips instanceof Object)) {
  1633. throw new TypeError('tooltips() expects parameter to be an Object');
  1634. }
  1635. $.extend(options.tooltips, tooltips);
  1636. if (widget) {
  1637. hide();
  1638. show();
  1639. }
  1640. return picker;
  1641. };
  1642. picker.useStrict = function (useStrict) {
  1643. if (arguments.length === 0) {
  1644. return options.useStrict;
  1645. }
  1646. if (typeof useStrict !== 'boolean') {
  1647. throw new TypeError('useStrict() expects a boolean parameter');
  1648. }
  1649. options.useStrict = useStrict;
  1650. return picker;
  1651. };
  1652. picker.sideBySide = function (sideBySide) {
  1653. if (arguments.length === 0) {
  1654. return options.sideBySide;
  1655. }
  1656. if (typeof sideBySide !== 'boolean') {
  1657. throw new TypeError('sideBySide() expects a boolean parameter');
  1658. }
  1659. options.sideBySide = sideBySide;
  1660. if (widget) {
  1661. hide();
  1662. show();
  1663. }
  1664. return picker;
  1665. };
  1666. picker.viewMode = function (viewMode) {
  1667. if (arguments.length === 0) {
  1668. return options.viewMode;
  1669. }
  1670. if (typeof viewMode !== 'string') {
  1671. throw new TypeError('viewMode() expects a string parameter');
  1672. }
  1673. if (viewModes.indexOf(viewMode) === -1) {
  1674. throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value');
  1675. }
  1676. options.viewMode = viewMode;
  1677. currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber);
  1678. showMode();
  1679. return picker;
  1680. };
  1681. picker.toolbarPlacement = function (toolbarPlacement) {
  1682. if (arguments.length === 0) {
  1683. return options.toolbarPlacement;
  1684. }
  1685. if (typeof toolbarPlacement !== 'string') {
  1686. throw new TypeError('toolbarPlacement() expects a string parameter');
  1687. }
  1688. if (toolbarPlacements.indexOf(toolbarPlacement) === -1) {
  1689. throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value');
  1690. }
  1691. options.toolbarPlacement = toolbarPlacement;
  1692. if (widget) {
  1693. hide();
  1694. show();
  1695. }
  1696. return picker;
  1697. };
  1698. picker.widgetPositioning = function (widgetPositioning) {
  1699. if (arguments.length === 0) {
  1700. return $.extend({}, options.widgetPositioning);
  1701. }
  1702. if (({}).toString.call(widgetPositioning) !== '[object Object]') {
  1703. throw new TypeError('widgetPositioning() expects an object variable');
  1704. }
  1705. if (widgetPositioning.horizontal) {
  1706. if (typeof widgetPositioning.horizontal !== 'string') {
  1707. throw new TypeError('widgetPositioning() horizontal variable must be a string');
  1708. }
  1709. widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase();
  1710. if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) {
  1711. throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')');
  1712. }
  1713. options.widgetPositioning.horizontal = widgetPositioning.horizontal;
  1714. }
  1715. if (widgetPositioning.vertical) {
  1716. if (typeof widgetPositioning.vertical !== 'string') {
  1717. throw new TypeError('widgetPositioning() vertical variable must be a string');
  1718. }
  1719. widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase();
  1720. if (verticalModes.indexOf(widgetPositioning.vertical) === -1) {
  1721. throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')');
  1722. }
  1723. options.widgetPositioning.vertical = widgetPositioning.vertical;
  1724. }
  1725. update();
  1726. return picker;
  1727. };
  1728. picker.calendarWeeks = function (calendarWeeks) {
  1729. if (arguments.length === 0) {
  1730. return options.calendarWeeks;
  1731. }
  1732. if (typeof calendarWeeks !== 'boolean') {
  1733. throw new TypeError('calendarWeeks() expects parameter to be a boolean value');
  1734. }
  1735. options.calendarWeeks = calendarWeeks;
  1736. update();
  1737. return picker;
  1738. };
  1739. picker.showTodayButton = function (showTodayButton) {
  1740. if (arguments.length === 0) {
  1741. return options.showTodayButton;
  1742. }
  1743. if (typeof showTodayButton !== 'boolean') {
  1744. throw new TypeError('showTodayButton() expects a boolean parameter');
  1745. }
  1746. options.showTodayButton = showTodayButton;
  1747. if (widget) {
  1748. hide();
  1749. show();
  1750. }
  1751. return picker;
  1752. };
  1753. picker.showClear = function (showClear) {
  1754. if (arguments.length === 0) {
  1755. return options.showClear;
  1756. }
  1757. if (typeof showClear !== 'boolean') {
  1758. throw new TypeError('showClear() expects a boolean parameter');
  1759. }
  1760. options.showClear = showClear;
  1761. if (widget) {
  1762. hide();
  1763. show();
  1764. }
  1765. return picker;
  1766. };
  1767. picker.widgetParent = function (widgetParent) {
  1768. if (arguments.length === 0) {
  1769. return options.widgetParent;
  1770. }
  1771. if (typeof widgetParent === 'string') {
  1772. widgetParent = $(widgetParent);
  1773. }
  1774. if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) {
  1775. throw new TypeError('widgetParent() expects a string or a jQuery object parameter');
  1776. }
  1777. options.widgetParent = widgetParent;
  1778. if (widget) {
  1779. hide();
  1780. show();
  1781. }
  1782. return picker;
  1783. };
  1784. picker.keepOpen = function (keepOpen) {
  1785. if (arguments.length === 0) {
  1786. return options.keepOpen;
  1787. }
  1788. if (typeof keepOpen !== 'boolean') {
  1789. throw new TypeError('keepOpen() expects a boolean parameter');
  1790. }
  1791. options.keepOpen = keepOpen;
  1792. return picker;
  1793. };
  1794. picker.focusOnShow = function (focusOnShow) {
  1795. if (arguments.length === 0) {
  1796. return options.focusOnShow;
  1797. }
  1798. if (typeof focusOnShow !== 'boolean') {
  1799. throw new TypeError('focusOnShow() expects a boolean parameter');
  1800. }
  1801. options.focusOnShow = focusOnShow;
  1802. return picker;
  1803. };
  1804. picker.inline = function (inline) {
  1805. if (arguments.length === 0) {
  1806. return options.inline;
  1807. }
  1808. if (typeof inline !== 'boolean') {
  1809. throw new TypeError('inline() expects a boolean parameter');
  1810. }
  1811. options.inline = inline;
  1812. return picker;
  1813. };
  1814. picker.clear = function () {
  1815. clear();
  1816. return picker;
  1817. };
  1818. picker.keyBinds = function (keyBinds) {
  1819. if (arguments.length === 0) {
  1820. return options.keyBinds;
  1821. }
  1822. options.keyBinds = keyBinds;
  1823. return picker;
  1824. };
  1825. picker.getMoment = function (d) {
  1826. return getMoment(d);
  1827. };
  1828. picker.debug = function (debug) {
  1829. if (typeof debug !== 'boolean') {
  1830. throw new TypeError('debug() expects a boolean parameter');
  1831. }
  1832. options.debug = debug;
  1833. return picker;
  1834. };
  1835. picker.allowInputToggle = function (allowInputToggle) {
  1836. if (arguments.length === 0) {
  1837. return options.allowInputToggle;
  1838. }
  1839. if (typeof allowInputToggle !== 'boolean') {
  1840. throw new TypeError('allowInputToggle() expects a boolean parameter');
  1841. }
  1842. options.allowInputToggle = allowInputToggle;
  1843. return picker;
  1844. };
  1845. picker.showClose = function (showClose) {
  1846. if (arguments.length === 0) {
  1847. return options.showClose;
  1848. }
  1849. if (typeof showClose !== 'boolean') {
  1850. throw new TypeError('showClose() expects a boolean parameter');
  1851. }
  1852. options.showClose = showClose;
  1853. return picker;
  1854. };
  1855. picker.keepInvalid = function (keepInvalid) {
  1856. if (arguments.length === 0) {
  1857. return options.keepInvalid;
  1858. }
  1859. if (typeof keepInvalid !== 'boolean') {
  1860. throw new TypeError('keepInvalid() expects a boolean parameter');
  1861. }
  1862. options.keepInvalid = keepInvalid;
  1863. return picker;
  1864. };
  1865. picker.datepickerInput = function (datepickerInput) {
  1866. if (arguments.length === 0) {
  1867. return options.datepickerInput;
  1868. }
  1869. if (typeof datepickerInput !== 'string') {
  1870. throw new TypeError('datepickerInput() expects a string parameter');
  1871. }
  1872. options.datepickerInput = datepickerInput;
  1873. return picker;
  1874. };
  1875. picker.parseInputDate = function (parseInputDate) {
  1876. if (arguments.length === 0) {
  1877. return options.parseInputDate;
  1878. }
  1879. if (typeof parseInputDate !== 'function') {
  1880. throw new TypeError('parseInputDate() sholud be as function');
  1881. }
  1882. options.parseInputDate = parseInputDate;
  1883. return picker;
  1884. };
  1885. picker.disabledTimeIntervals = function (disabledTimeIntervals) {
  1886. ///<signature helpKeyword="$.fn.datetimepicker.disabledTimeIntervals">
  1887. ///<summary>Returns an array with the currently set disabled dates on the component.</summary>
  1888. ///<returns type="array">options.disabledTimeIntervals</returns>
  1889. ///</signature>
  1890. ///<signature>
  1891. ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
  1892. ///options.enabledDates if such exist.</summary>
  1893. ///<param name="dates" locid="$.fn.datetimepicker.disabledTimeIntervals_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
  1894. ///</signature>
  1895. if (arguments.length === 0) {
  1896. return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals);
  1897. }
  1898. if (!disabledTimeIntervals) {
  1899. options.disabledTimeIntervals = false;
  1900. update();
  1901. return picker;
  1902. }
  1903. if (!(disabledTimeIntervals instanceof Array)) {
  1904. throw new TypeError('disabledTimeIntervals() expects an array parameter');
  1905. }
  1906. options.disabledTimeIntervals = disabledTimeIntervals;
  1907. update();
  1908. return picker;
  1909. };
  1910. picker.disabledHours = function (hours) {
  1911. ///<signature helpKeyword="$.fn.datetimepicker.disabledHours">
  1912. ///<summary>Returns an array with the currently set disabled hours on the component.</summary>
  1913. ///<returns type="array">options.disabledHours</returns>
  1914. ///</signature>
  1915. ///<signature>
  1916. ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
  1917. ///options.enabledHours if such exist.</summary>
  1918. ///<param name="hours" locid="$.fn.datetimepicker.disabledHours_p:hours">Takes an [ int ] of values and disallows the user to select only from those hours.</param>
  1919. ///</signature>
  1920. if (arguments.length === 0) {
  1921. return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours);
  1922. }
  1923. if (!hours) {
  1924. options.disabledHours = false;
  1925. update();
  1926. return picker;
  1927. }
  1928. if (!(hours instanceof Array)) {
  1929. throw new TypeError('disabledHours() expects an array parameter');
  1930. }
  1931. options.disabledHours = indexGivenHours(hours);
  1932. options.enabledHours = false;
  1933. if (options.useCurrent && !options.keepInvalid) {
  1934. var tries = 0;
  1935. while (!isValid(date, 'h')) {
  1936. date.add(1, 'h');
  1937. if (tries === 24) {
  1938. throw 'Tried 24 times to find a valid date';
  1939. }
  1940. tries++;
  1941. }
  1942. setValue(date);
  1943. }
  1944. update();
  1945. return picker;
  1946. };
  1947. picker.enabledHours = function (hours) {
  1948. ///<signature helpKeyword="$.fn.datetimepicker.enabledHours">
  1949. ///<summary>Returns an array with the currently set enabled hours on the component.</summary>
  1950. ///<returns type="array">options.enabledHours</returns>
  1951. ///</signature>
  1952. ///<signature>
  1953. ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist.</summary>
  1954. ///<param name="hours" locid="$.fn.datetimepicker.enabledHours_p:hours">Takes an [ int ] of values and allows the user to select only from those hours.</param>
  1955. ///</signature>
  1956. if (arguments.length === 0) {
  1957. return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours);
  1958. }
  1959. if (!hours) {
  1960. options.enabledHours = false;
  1961. update();
  1962. return picker;
  1963. }
  1964. if (!(hours instanceof Array)) {
  1965. throw new TypeError('enabledHours() expects an array parameter');
  1966. }
  1967. options.enabledHours = indexGivenHours(hours);
  1968. options.disabledHours = false;
  1969. if (options.useCurrent && !options.keepInvalid) {
  1970. var tries = 0;
  1971. while (!isValid(date, 'h')) {
  1972. date.add(1, 'h');
  1973. if (tries === 24) {
  1974. throw 'Tried 24 times to find a valid date';
  1975. }
  1976. tries++;
  1977. }
  1978. setValue(date);
  1979. }
  1980. update();
  1981. return picker;
  1982. };
  1983. /**
  1984. * Returns the component's model current viewDate, a moment object or null if not set. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.
  1985. * @param {Takes string, viewDate, moment, null parameter.} newDate
  1986. * @returns {viewDate.clone()}
  1987. */
  1988. picker.viewDate = function (newDate) {
  1989. if (arguments.length === 0) {
  1990. return viewDate.clone();
  1991. }
  1992. if (!newDate) {
  1993. viewDate = date.clone();
  1994. return picker;
  1995. }
  1996. if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
  1997. throw new TypeError('viewDate() parameter must be one of [string, moment or Date]');
  1998. }
  1999. viewDate = parseInputDate(newDate);
  2000. viewUpdate();
  2001. return picker;
  2002. };
  2003. // initializing element and component attributes
  2004. if (element.is('input')) {
  2005. input = element;
  2006. } else {
  2007. input = element.find(options.datepickerInput);
  2008. if (input.length === 0) {
  2009. input = element.find('input');
  2010. } else if (!input.is('input')) {
  2011. throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element');
  2012. }
  2013. }
  2014. if (element.hasClass('input-group')) {
  2015. // in case there is more then one 'input-group-addon' Issue #48
  2016. if (element.find('.datepickerbutton').length === 0) {
  2017. component = element.find('.input-group-addon');
  2018. } else {
  2019. component = element.find('.datepickerbutton');
  2020. }
  2021. }
  2022. if (!options.inline && !input.is('input')) {
  2023. throw new Error('Could not initialize DateTimePicker without an input element');
  2024. }
  2025. // Set defaults for date here now instead of in var declaration
  2026. date = getMoment();
  2027. viewDate = date.clone();
  2028. $.extend(true, options, dataToOptions());
  2029. picker.options(options);
  2030. initFormatting();
  2031. attachDatePickerElementEvents();
  2032. if (input.prop('disabled')) {
  2033. picker.disable();
  2034. }
  2035. if (input.is('input') && input.val().trim().length !== 0) {
  2036. setValue(parseInputDate(input.val().trim()));
  2037. }
  2038. else if (options.defaultDate && input.attr('placeholder') === undefined) {
  2039. setValue(options.defaultDate);
  2040. }
  2041. if (options.inline) {
  2042. show();
  2043. }
  2044. return picker;
  2045. };
  2046. /********************************************************************************
  2047. *
  2048. * jQuery plugin constructor and defaults object
  2049. *
  2050. ********************************************************************************/
  2051. /**
  2052. * See (http://jquery.com/).
  2053. * @name jQuery
  2054. * @class
  2055. * See the jQuery Library (http://jquery.com/) for full details. This just
  2056. * documents the function and classes that are added to jQuery by this plug-in.
  2057. */
  2058. /**
  2059. * See (http://jquery.com/)
  2060. * @name fn
  2061. * @class
  2062. * See the jQuery Library (http://jquery.com/) for full details. This just
  2063. * documents the function and classes that are added to jQuery by this plug-in.
  2064. * @memberOf jQuery
  2065. */
  2066. /**
  2067. * Show comments
  2068. * @class datetimepicker
  2069. * @memberOf jQuery.fn
  2070. */
  2071. $.fn.datetimepicker = function (options) {
  2072. options = options || {};
  2073. var args = Array.prototype.slice.call(arguments, 1),
  2074. isInstance = true,
  2075. thisMethods = ['destroy', 'hide', 'show', 'toggle'],
  2076. returnValue;
  2077. if (typeof options === 'object') {
  2078. return this.each(function () {
  2079. var $this = $(this),
  2080. _options;
  2081. if (!$this.data('DateTimePicker')) {
  2082. // create a private copy of the defaults object
  2083. _options = $.extend(true, {}, $.fn.datetimepicker.defaults, options);
  2084. $this.data('DateTimePicker', dateTimePicker($this, _options));
  2085. }
  2086. });
  2087. } else if (typeof options === 'string') {
  2088. this.each(function () {
  2089. var $this = $(this),
  2090. instance = $this.data('DateTimePicker');
  2091. if (!instance) {
  2092. throw new Error('bootstrap-datetimepicker("' + options + '") method was called on an element that is not using DateTimePicker');
  2093. }
  2094. returnValue = instance[options].apply(instance, args);
  2095. isInstance = returnValue === instance;
  2096. });
  2097. if (isInstance || $.inArray(options, thisMethods) > -1) {
  2098. return this;
  2099. }
  2100. return returnValue;
  2101. }
  2102. throw new TypeError('Invalid arguments for DateTimePicker: ' + options);
  2103. };
  2104. $.fn.datetimepicker.defaults = {
  2105. timeZone: '',
  2106. format: false,
  2107. dayViewHeaderFormat: 'MMMM YYYY',
  2108. extraFormats: false,
  2109. stepping: 1,
  2110. minDate: false,
  2111. maxDate: false,
  2112. useCurrent: true,
  2113. collapse: true,
  2114. locale: moment.locale(),
  2115. defaultDate: false,
  2116. disabledDates: false,
  2117. enabledDates: false,
  2118. icons: {
  2119. time: 'glyphicon glyphicon-time',
  2120. date: 'glyphicon glyphicon-calendar',
  2121. up: 'glyphicon glyphicon-chevron-up',
  2122. down: 'glyphicon glyphicon-chevron-down',
  2123. previous: 'glyphicon glyphicon-chevron-left',
  2124. next: 'glyphicon glyphicon-chevron-right',
  2125. today: 'glyphicon glyphicon-screenshot',
  2126. clear: 'glyphicon glyphicon-trash',
  2127. close: 'glyphicon glyphicon-remove'
  2128. },
  2129. tooltips: {
  2130. today: 'Go to today',
  2131. clear: 'Clear selection',
  2132. close: 'Close the picker',
  2133. selectMonth: 'Select Month',
  2134. prevMonth: 'Previous Month',
  2135. nextMonth: 'Next Month',
  2136. selectYear: 'Select Year',
  2137. prevYear: 'Previous Year',
  2138. nextYear: 'Next Year',
  2139. selectDecade: 'Select Decade',
  2140. prevDecade: 'Previous Decade',
  2141. nextDecade: 'Next Decade',
  2142. prevCentury: 'Previous Century',
  2143. nextCentury: 'Next Century',
  2144. pickHour: 'Pick Hour',
  2145. incrementHour: 'Increment Hour',
  2146. decrementHour: 'Decrement Hour',
  2147. pickMinute: 'Pick Minute',
  2148. incrementMinute: 'Increment Minute',
  2149. decrementMinute: 'Decrement Minute',
  2150. pickSecond: 'Pick Second',
  2151. incrementSecond: 'Increment Second',
  2152. decrementSecond: 'Decrement Second',
  2153. togglePeriod: 'Toggle Period',
  2154. selectTime: 'Select Time'
  2155. },
  2156. useStrict: false,
  2157. sideBySide: false,
  2158. daysOfWeekDisabled: false,
  2159. calendarWeeks: false,
  2160. viewMode: 'days',
  2161. toolbarPlacement: 'default',
  2162. showTodayButton: false,
  2163. showClear: false,
  2164. showClose: false,
  2165. widgetPositioning: {
  2166. horizontal: 'auto',
  2167. vertical: 'auto'
  2168. },
  2169. widgetParent: null,
  2170. ignoreReadonly: false,
  2171. keepOpen: false,
  2172. focusOnShow: true,
  2173. inline: false,
  2174. keepInvalid: false,
  2175. datepickerInput: '.datepickerinput',
  2176. keyBinds: {
  2177. up: function (widget) {
  2178. if (!widget) {
  2179. return;
  2180. }
  2181. var d = this.date() || this.getMoment();
  2182. if (widget.find('.datepicker').is(':visible')) {
  2183. this.date(d.clone().subtract(7, 'd'));
  2184. } else {
  2185. this.date(d.clone().add(this.stepping(), 'm'));
  2186. }
  2187. },
  2188. down: function (widget) {
  2189. if (!widget) {
  2190. this.show();
  2191. return;
  2192. }
  2193. var d = this.date() || this.getMoment();
  2194. if (widget.find('.datepicker').is(':visible')) {
  2195. this.date(d.clone().add(7, 'd'));
  2196. } else {
  2197. this.date(d.clone().subtract(this.stepping(), 'm'));
  2198. }
  2199. },
  2200. 'control up': function (widget) {
  2201. if (!widget) {
  2202. return;
  2203. }
  2204. var d = this.date() || this.getMoment();
  2205. if (widget.find('.datepicker').is(':visible')) {
  2206. this.date(d.clone().subtract(1, 'y'));
  2207. } else {
  2208. this.date(d.clone().add(1, 'h'));
  2209. }
  2210. },
  2211. 'control down': function (widget) {
  2212. if (!widget) {
  2213. return;
  2214. }
  2215. var d = this.date() || this.getMoment();
  2216. if (widget.find('.datepicker').is(':visible')) {
  2217. this.date(d.clone().add(1, 'y'));
  2218. } else {
  2219. this.date(d.clone().subtract(1, 'h'));
  2220. }
  2221. },
  2222. left: function (widget) {
  2223. if (!widget) {
  2224. return;
  2225. }
  2226. var d = this.date() || this.getMoment();
  2227. if (widget.find('.datepicker').is(':visible')) {
  2228. this.date(d.clone().subtract(1, 'd'));
  2229. }
  2230. },
  2231. right: function (widget) {
  2232. if (!widget) {
  2233. return;
  2234. }
  2235. var d = this.date() || this.getMoment();
  2236. if (widget.find('.datepicker').is(':visible')) {
  2237. this.date(d.clone().add(1, 'd'));
  2238. }
  2239. },
  2240. pageUp: function (widget) {
  2241. if (!widget) {
  2242. return;
  2243. }
  2244. var d = this.date() || this.getMoment();
  2245. if (widget.find('.datepicker').is(':visible')) {
  2246. this.date(d.clone().subtract(1, 'M'));
  2247. }
  2248. },
  2249. pageDown: function (widget) {
  2250. if (!widget) {
  2251. return;
  2252. }
  2253. var d = this.date() || this.getMoment();
  2254. if (widget.find('.datepicker').is(':visible')) {
  2255. this.date(d.clone().add(1, 'M'));
  2256. }
  2257. },
  2258. enter: function () {
  2259. this.hide();
  2260. },
  2261. escape: function () {
  2262. this.hide();
  2263. },
  2264. //tab: function (widget) { //this break the flow of the form. disabling for now
  2265. // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]');
  2266. // if(toggle.length > 0) toggle.click();
  2267. //},
  2268. 'control space': function (widget) {
  2269. if (!widget) {
  2270. return;
  2271. }
  2272. if (widget.find('.timepicker').is(':visible')) {
  2273. widget.find('.btn[data-action="togglePeriod"]').click();
  2274. }
  2275. },
  2276. t: function () {
  2277. this.date(this.getMoment());
  2278. },
  2279. 'delete': function () {
  2280. this.clear();
  2281. }
  2282. },
  2283. debug: false,
  2284. allowInputToggle: false,
  2285. disabledTimeIntervals: false,
  2286. disabledHours: false,
  2287. enabledHours: false,
  2288. viewDate: false
  2289. };
  2290. return $.fn.datetimepicker;
  2291. }));