Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

2281 linhas
77 KiB

  1. ;(function (window, $, undefined) { ;(function () {
  2. var VERSION = '2.2.3',
  3. pluginName = 'datepicker',
  4. autoInitSelector = '.datepicker-here',
  5. $body, $datepickersContainer,
  6. containerBuilt = false,
  7. baseTemplate = '' +
  8. '<div class="datepicker">' +
  9. '<i class="datepicker--pointer"></i>' +
  10. '<nav class="datepicker--nav"></nav>' +
  11. '<div class="datepicker--content"></div>' +
  12. '</div>',
  13. defaults = {
  14. classes: '',
  15. inline: false,
  16. language: 'ru',
  17. startDate: new Date(),
  18. firstDay: '',
  19. weekends: [6, 0],
  20. dateFormat: '',
  21. altField: '',
  22. altFieldDateFormat: '@',
  23. toggleSelected: true,
  24. keyboardNav: true,
  25. position: 'bottom left',
  26. offset: 12,
  27. view: 'days',
  28. minView: 'days',
  29. showOtherMonths: true,
  30. selectOtherMonths: true,
  31. moveToOtherMonthsOnSelect: true,
  32. showOtherYears: true,
  33. selectOtherYears: true,
  34. moveToOtherYearsOnSelect: true,
  35. minDate: '',
  36. maxDate: '',
  37. disableNavWhenOutOfRange: true,
  38. multipleDates: false, // Boolean or Number
  39. multipleDatesSeparator: ',',
  40. range: false,
  41. todayButton: false,
  42. clearButton: false,
  43. showEvent: 'focus',
  44. autoClose: false,
  45. // navigation
  46. monthsField: 'monthsShort',
  47. prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>',
  48. nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>',
  49. navTitles: {
  50. days: 'MM, <i>yyyy</i>',
  51. months: 'yyyy',
  52. years: 'yyyy1 - yyyy2'
  53. },
  54. // timepicker
  55. timepicker: false,
  56. onlyTimepicker: false,
  57. dateTimeSeparator: ' ',
  58. timeFormat: '',
  59. minHours: 0,
  60. maxHours: 24,
  61. minMinutes: 0,
  62. maxMinutes: 59,
  63. minSeconds: 0,
  64. maxSeconds: 59,
  65. hoursStep: 1,
  66. minutesStep: 1,
  67. secondsStep: 1,
  68. // events
  69. onSelect: '',
  70. onShow: '',
  71. onHide: '',
  72. onChangeMonth: '',
  73. onChangeYear: '',
  74. onChangeDecade: '',
  75. onChangeView: '',
  76. onRenderCell: ''
  77. },
  78. hotKeys = {
  79. 'ctrlRight': [17, 39],
  80. 'ctrlUp': [17, 38],
  81. 'ctrlLeft': [17, 37],
  82. 'ctrlDown': [17, 40],
  83. 'shiftRight': [16, 39],
  84. 'shiftUp': [16, 38],
  85. 'shiftLeft': [16, 37],
  86. 'shiftDown': [16, 40],
  87. 'altUp': [18, 38],
  88. 'altRight': [18, 39],
  89. 'altLeft': [18, 37],
  90. 'altDown': [18, 40],
  91. 'ctrlShiftUp': [16, 17, 38]
  92. },
  93. datepicker;
  94. var Datepicker = function (el, options) {
  95. this.el = el;
  96. this.$el = $(el);
  97. this.opts = $.extend(true, {}, defaults, options, this.$el.data());
  98. if ($body == undefined) {
  99. $body = $('body');
  100. }
  101. if (!this.opts.startDate) {
  102. this.opts.startDate = new Date();
  103. }
  104. if (this.el.nodeName == 'INPUT') {
  105. this.elIsInput = true;
  106. }
  107. if (this.opts.altField) {
  108. this.$altField = typeof this.opts.altField == 'string' ? $(this.opts.altField) : this.opts.altField;
  109. }
  110. this.inited = false;
  111. this.visible = false;
  112. this.silent = false; // Need to prevent unnecessary rendering
  113. this.currentDate = this.opts.startDate;
  114. this.currentView = this.opts.view;
  115. this._createShortCuts();
  116. this.selectedDates = [];
  117. this.views = {};
  118. this.keys = [];
  119. this.minRange = '';
  120. this.maxRange = '';
  121. this._prevOnSelectValue = '';
  122. this.init()
  123. };
  124. datepicker = Datepicker;
  125. datepicker.prototype = {
  126. VERSION: VERSION,
  127. viewIndexes: ['days', 'months', 'years'],
  128. init: function () {
  129. if (!containerBuilt && !this.opts.inline && this.elIsInput) {
  130. this._buildDatepickersContainer();
  131. }
  132. this._buildBaseHtml();
  133. this._defineLocale(this.opts.language);
  134. this._syncWithMinMaxDates();
  135. if (this.elIsInput) {
  136. if (!this.opts.inline) {
  137. // Set extra classes for proper transitions
  138. this._setPositionClasses(this.opts.position);
  139. this._bindEvents()
  140. }
  141. if (this.opts.keyboardNav && !this.opts.onlyTimepicker) {
  142. this._bindKeyboardEvents();
  143. }
  144. this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this));
  145. this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this));
  146. }
  147. if (this.opts.classes) {
  148. this.$datepicker.addClass(this.opts.classes)
  149. }
  150. if (this.opts.timepicker) {
  151. this.timepicker = new $.fn.datepicker.Timepicker(this, this.opts);
  152. this._bindTimepickerEvents();
  153. }
  154. if (this.opts.onlyTimepicker) {
  155. this.$datepicker.addClass('-only-timepicker-');
  156. }
  157. this.views[this.currentView] = new $.fn.datepicker.Body(this, this.currentView, this.opts);
  158. this.views[this.currentView].show();
  159. this.nav = new $.fn.datepicker.Navigation(this, this.opts);
  160. this.view = this.currentView;
  161. this.$el.on('clickCell.adp', this._onClickCell.bind(this));
  162. this.$datepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this));
  163. this.$datepicker.on('mouseleave', '.datepicker--cell', this._onMouseLeaveCell.bind(this));
  164. this.inited = true;
  165. },
  166. _createShortCuts: function () {
  167. this.minDate = this.opts.minDate ? this.opts.minDate : new Date(-8639999913600000);
  168. this.maxDate = this.opts.maxDate ? this.opts.maxDate : new Date(8639999913600000);
  169. },
  170. _bindEvents : function () {
  171. this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this));
  172. this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this));
  173. this.$el.on('blur.adp', this._onBlur.bind(this));
  174. this.$el.on('keyup.adp', this._onKeyUpGeneral.bind(this));
  175. $(window).on('resize.adp', this._onResize.bind(this));
  176. $('body').on('mouseup.adp', this._onMouseUpBody.bind(this));
  177. },
  178. _bindKeyboardEvents: function () {
  179. this.$el.on('keydown.adp', this._onKeyDown.bind(this));
  180. this.$el.on('keyup.adp', this._onKeyUp.bind(this));
  181. this.$el.on('hotKey.adp', this._onHotKey.bind(this));
  182. },
  183. _bindTimepickerEvents: function () {
  184. this.$el.on('timeChange.adp', this._onTimeChange.bind(this));
  185. },
  186. isWeekend: function (day) {
  187. return this.opts.weekends.indexOf(day) !== -1;
  188. },
  189. _defineLocale: function (lang) {
  190. if (typeof lang == 'string') {
  191. this.loc = $.fn.datepicker.language[lang];
  192. if (!this.loc) {
  193. console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "ru" instead');
  194. this.loc = $.extend(true, {}, $.fn.datepicker.language.ru)
  195. }
  196. this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, $.fn.datepicker.language[lang])
  197. } else {
  198. this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, lang)
  199. }
  200. if (this.opts.dateFormat) {
  201. this.loc.dateFormat = this.opts.dateFormat
  202. }
  203. if (this.opts.timeFormat) {
  204. this.loc.timeFormat = this.opts.timeFormat
  205. }
  206. if (this.opts.firstDay !== '') {
  207. this.loc.firstDay = this.opts.firstDay
  208. }
  209. if (this.opts.timepicker) {
  210. this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator);
  211. }
  212. if (this.opts.onlyTimepicker) {
  213. this.loc.dateFormat = this.loc.timeFormat;
  214. }
  215. var boundary = this._getWordBoundaryRegExp;
  216. if (this.loc.timeFormat.match(boundary('aa')) ||
  217. this.loc.timeFormat.match(boundary('AA'))
  218. ) {
  219. this.ampm = true;
  220. }
  221. },
  222. _buildDatepickersContainer: function () {
  223. containerBuilt = true;
  224. $body.append('<div class="datepickers-container" id="datepickers-container"></div>');
  225. $datepickersContainer = $('#datepickers-container');
  226. },
  227. _buildBaseHtml: function () {
  228. var $appendTarget,
  229. $inline = $('<div class="datepicker-inline">');
  230. if(this.el.nodeName == 'INPUT') {
  231. if (!this.opts.inline) {
  232. $appendTarget = $datepickersContainer;
  233. } else {
  234. $appendTarget = $inline.insertAfter(this.$el)
  235. }
  236. } else {
  237. $appendTarget = $inline.appendTo(this.$el)
  238. }
  239. this.$datepicker = $(baseTemplate).appendTo($appendTarget);
  240. this.$content = $('.datepicker--content', this.$datepicker);
  241. this.$nav = $('.datepicker--nav', this.$datepicker);
  242. },
  243. _triggerOnChange: function () {
  244. if (!this.selectedDates.length) {
  245. // Prevent from triggering multiple onSelect callback with same argument (empty string) in IE10-11
  246. if (this._prevOnSelectValue === '') return;
  247. this._prevOnSelectValue = '';
  248. return this.opts.onSelect('', '', this);
  249. }
  250. var selectedDates = this.selectedDates,
  251. parsedSelected = datepicker.getParsedDate(selectedDates[0]),
  252. formattedDates,
  253. _this = this,
  254. dates = new Date(
  255. parsedSelected.year,
  256. parsedSelected.month,
  257. parsedSelected.date,
  258. parsedSelected.hours,
  259. parsedSelected.minutes,
  260. parsedSelected.seconds
  261. );
  262. formattedDates = selectedDates.map(function (date) {
  263. return _this.formatDate(_this.loc.dateFormat, date)
  264. }).join(this.opts.multipleDatesSeparator);
  265. // Create new dates array, to separate it from original selectedDates
  266. if (this.opts.multipleDates || this.opts.range) {
  267. dates = selectedDates.map(function(date) {
  268. var parsedDate = datepicker.getParsedDate(date);
  269. return new Date(
  270. parsedDate.year,
  271. parsedDate.month,
  272. parsedDate.date,
  273. parsedDate.hours,
  274. parsedDate.minutes,
  275. parsedDate.seconds
  276. );
  277. })
  278. }
  279. this._prevOnSelectValue = formattedDates;
  280. this.opts.onSelect(formattedDates, dates, this);
  281. },
  282. next: function () {
  283. var d = this.parsedDate,
  284. o = this.opts;
  285. switch (this.view) {
  286. case 'days':
  287. this.date = new Date(d.year, d.month + 1, 1);
  288. if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
  289. break;
  290. case 'months':
  291. this.date = new Date(d.year + 1, d.month, 1);
  292. if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
  293. break;
  294. case 'years':
  295. this.date = new Date(d.year + 10, 0, 1);
  296. if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
  297. break;
  298. }
  299. },
  300. prev: function () {
  301. var d = this.parsedDate,
  302. o = this.opts;
  303. switch (this.view) {
  304. case 'days':
  305. this.date = new Date(d.year, d.month - 1, 1);
  306. if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
  307. break;
  308. case 'months':
  309. this.date = new Date(d.year - 1, d.month, 1);
  310. if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
  311. break;
  312. case 'years':
  313. this.date = new Date(d.year - 10, 0, 1);
  314. if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
  315. break;
  316. }
  317. },
  318. formatDate: function (string, date) {
  319. date = date || this.date;
  320. var result = string,
  321. boundary = this._getWordBoundaryRegExp,
  322. locale = this.loc,
  323. leadingZero = datepicker.getLeadingZeroNum,
  324. decade = datepicker.getDecade(date),
  325. d = datepicker.getParsedDate(date),
  326. fullHours = d.fullHours,
  327. hours = d.hours,
  328. ampm = string.match(boundary('aa')) || string.match(boundary('AA')),
  329. dayPeriod = 'am',
  330. replacer = this._replacer,
  331. validHours;
  332. if (this.opts.timepicker && this.timepicker && ampm) {
  333. validHours = this.timepicker._getValidHoursFromDate(date, ampm);
  334. fullHours = leadingZero(validHours.hours);
  335. hours = validHours.hours;
  336. dayPeriod = validHours.dayPeriod;
  337. }
  338. switch (true) {
  339. case /@/.test(result):
  340. result = result.replace(/@/, date.getTime());
  341. case /aa/.test(result):
  342. result = replacer(result, boundary('aa'), dayPeriod);
  343. case /AA/.test(result):
  344. result = replacer(result, boundary('AA'), dayPeriod.toUpperCase());
  345. case /dd/.test(result):
  346. result = replacer(result, boundary('dd'), d.fullDate);
  347. case /d/.test(result):
  348. result = replacer(result, boundary('d'), d.date);
  349. case /DD/.test(result):
  350. result = replacer(result, boundary('DD'), locale.days[d.day]);
  351. case /D/.test(result):
  352. result = replacer(result, boundary('D'), locale.daysShort[d.day]);
  353. case /mm/.test(result):
  354. result = replacer(result, boundary('mm'), d.fullMonth);
  355. case /m/.test(result):
  356. result = replacer(result, boundary('m'), d.month + 1);
  357. case /MM/.test(result):
  358. result = replacer(result, boundary('MM'), this.loc.months[d.month]);
  359. case /M/.test(result):
  360. result = replacer(result, boundary('M'), locale.monthsShort[d.month]);
  361. case /ss/.test(result):
  362. result = replacer(result, boundary('ss'), d.fullSeconds);
  363. case /s/.test(result):
  364. result = replacer(result, boundary('s'), d.seconds);
  365. case /ii/.test(result):
  366. result = replacer(result, boundary('ii'), d.fullMinutes);
  367. case /i/.test(result):
  368. result = replacer(result, boundary('i'), d.minutes);
  369. case /hh/.test(result):
  370. result = replacer(result, boundary('hh'), fullHours);
  371. case /h/.test(result):
  372. result = replacer(result, boundary('h'), hours);
  373. case /yyyy/.test(result):
  374. result = replacer(result, boundary('yyyy'), d.year);
  375. case /yyyy1/.test(result):
  376. result = replacer(result, boundary('yyyy1'), decade[0]);
  377. case /yyyy2/.test(result):
  378. result = replacer(result, boundary('yyyy2'), decade[1]);
  379. case /yy/.test(result):
  380. result = replacer(result, boundary('yy'), d.year.toString().slice(-2));
  381. }
  382. return result;
  383. },
  384. _replacer: function (str, reg, data) {
  385. return str.replace(reg, function (match, p1,p2,p3) {
  386. return p1 + data + p3;
  387. })
  388. },
  389. _getWordBoundaryRegExp: function (sign) {
  390. var symbols = '\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;';
  391. return new RegExp('(^|>|' + symbols + ')(' + sign + ')($|<|' + symbols + ')', 'g');
  392. },
  393. selectDate: function (date) {
  394. var _this = this,
  395. opts = _this.opts,
  396. d = _this.parsedDate,
  397. selectedDates = _this.selectedDates,
  398. len = selectedDates.length,
  399. newDate = '';
  400. if (Array.isArray(date)) {
  401. date.forEach(function (d) {
  402. _this.selectDate(d)
  403. });
  404. return;
  405. }
  406. if (!(date instanceof Date)) return;
  407. this.lastSelectedDate = date;
  408. // Set new time values from Date
  409. if (this.timepicker) {
  410. this.timepicker._setTime(date);
  411. }
  412. // On this step timepicker will set valid values in it's instance
  413. _this._trigger('selectDate', date);
  414. // Set correct time values after timepicker's validation
  415. // Prevent from setting hours or minutes which values are lesser then `min` value or
  416. // greater then `max` value
  417. if (this.timepicker) {
  418. date.setHours(this.timepicker.hours);
  419. date.setMinutes(this.timepicker.minutes)
  420. date.setSeconds(this.timepicker.seconds)
  421. }
  422. if (_this.view == 'days') {
  423. if (date.getMonth() != d.month && opts.moveToOtherMonthsOnSelect) {
  424. newDate = new Date(date.getFullYear(), date.getMonth(), 1);
  425. }
  426. }
  427. if (_this.view == 'years') {
  428. if (date.getFullYear() != d.year && opts.moveToOtherYearsOnSelect) {
  429. newDate = new Date(date.getFullYear(), 0, 1);
  430. }
  431. }
  432. if (newDate) {
  433. _this.silent = true;
  434. _this.date = newDate;
  435. _this.silent = false;
  436. _this.nav._render()
  437. }
  438. if (opts.multipleDates && !opts.range) { // Set priority to range functionality
  439. if (len === opts.multipleDates) return;
  440. if (!_this._isSelected(date)) {
  441. _this.selectedDates.push(date);
  442. }
  443. } else if (opts.range) {
  444. if (len == 2) {
  445. _this.selectedDates = [date];
  446. _this.minRange = date;
  447. _this.maxRange = '';
  448. } else if (len == 1) {
  449. _this.selectedDates.push(date);
  450. if (!_this.maxRange){
  451. _this.maxRange = date;
  452. } else {
  453. _this.minRange = date;
  454. }
  455. // Swap dates if they were selected via dp.selectDate() and second date was smaller then first
  456. if (datepicker.bigger(_this.maxRange, _this.minRange)) {
  457. _this.maxRange = _this.minRange;
  458. _this.minRange = date;
  459. }
  460. _this.selectedDates = [_this.minRange, _this.maxRange]
  461. } else {
  462. _this.selectedDates = [date];
  463. _this.minRange = date;
  464. }
  465. } else {
  466. _this.selectedDates = [date];
  467. }
  468. _this._setInputValue();
  469. if (opts.onSelect) {
  470. _this._triggerOnChange();
  471. }
  472. if (opts.autoClose && !this.timepickerIsActive) {
  473. if (!opts.multipleDates && !opts.range) {
  474. _this.hide();
  475. } else if (opts.range && _this.selectedDates.length == 2) {
  476. _this.hide();
  477. }
  478. }
  479. _this.views[this.currentView]._render()
  480. },
  481. removeDate: function (date) {
  482. var selected = this.selectedDates,
  483. _this = this;
  484. if (!(date instanceof Date)) return;
  485. return selected.some(function (curDate, i) {
  486. if (datepicker.isSame(curDate, date)) {
  487. selected.splice(i, 1);
  488. if (!_this.selectedDates.length) {
  489. _this.minRange = '';
  490. _this.maxRange = '';
  491. _this.lastSelectedDate = '';
  492. } else {
  493. _this.lastSelectedDate = _this.selectedDates[_this.selectedDates.length - 1];
  494. }
  495. _this.views[_this.currentView]._render();
  496. _this._setInputValue();
  497. if (_this.opts.onSelect) {
  498. _this._triggerOnChange();
  499. }
  500. return true
  501. }
  502. })
  503. },
  504. today: function () {
  505. this.silent = true;
  506. this.view = this.opts.minView;
  507. this.silent = false;
  508. this.date = new Date();
  509. if (this.opts.todayButton instanceof Date) {
  510. this.selectDate(this.opts.todayButton)
  511. }
  512. },
  513. clear: function () {
  514. this.selectedDates = [];
  515. this.minRange = '';
  516. this.maxRange = '';
  517. this.views[this.currentView]._render();
  518. this._setInputValue();
  519. if (this.opts.onSelect) {
  520. this._triggerOnChange()
  521. }
  522. },
  523. /**
  524. * Updates datepicker options
  525. * @param {String|Object} param - parameter's name to update. If object then it will extend current options
  526. * @param {String|Number|Object} [value] - new param value
  527. */
  528. update: function (param, value) {
  529. var len = arguments.length,
  530. lastSelectedDate = this.lastSelectedDate;
  531. if (len == 2) {
  532. this.opts[param] = value;
  533. } else if (len == 1 && typeof param == 'object') {
  534. this.opts = $.extend(true, this.opts, param)
  535. }
  536. this._createShortCuts();
  537. this._syncWithMinMaxDates();
  538. this._defineLocale(this.opts.language);
  539. this.nav._addButtonsIfNeed();
  540. if (!this.opts.onlyTimepicker) this.nav._render();
  541. this.views[this.currentView]._render();
  542. if (this.elIsInput && !this.opts.inline) {
  543. this._setPositionClasses(this.opts.position);
  544. if (this.visible) {
  545. this.setPosition(this.opts.position)
  546. }
  547. }
  548. if (this.opts.classes) {
  549. this.$datepicker.addClass(this.opts.classes)
  550. }
  551. if (this.opts.onlyTimepicker) {
  552. this.$datepicker.addClass('-only-timepicker-');
  553. }
  554. if (this.opts.timepicker) {
  555. if (lastSelectedDate) this.timepicker._handleDate(lastSelectedDate);
  556. this.timepicker._updateRanges();
  557. this.timepicker._updateCurrentTime();
  558. // Change hours and minutes if it's values have been changed through min/max hours/minutes
  559. if (lastSelectedDate) {
  560. lastSelectedDate.setHours(this.timepicker.hours);
  561. lastSelectedDate.setMinutes(this.timepicker.minutes);
  562. lastSelectedDate.setSeconds(this.timepicker.seconds);
  563. }
  564. }
  565. this._setInputValue();
  566. return this;
  567. },
  568. _syncWithMinMaxDates: function () {
  569. var curTime = this.date.getTime();
  570. this.silent = true;
  571. if (this.minTime > curTime) {
  572. this.date = this.minDate;
  573. }
  574. if (this.maxTime < curTime) {
  575. this.date = this.maxDate;
  576. }
  577. this.silent = false;
  578. },
  579. _isSelected: function (checkDate, cellType) {
  580. var res = false;
  581. this.selectedDates.some(function (date) {
  582. if (datepicker.isSame(date, checkDate, cellType)) {
  583. res = date;
  584. return true;
  585. }
  586. });
  587. return res;
  588. },
  589. _setInputValue: function () {
  590. var _this = this,
  591. opts = _this.opts,
  592. format = _this.loc.dateFormat,
  593. altFormat = opts.altFieldDateFormat,
  594. value = _this.selectedDates.map(function (date) {
  595. return _this.formatDate(format, date)
  596. }),
  597. altValues;
  598. if (opts.altField && _this.$altField.length) {
  599. altValues = this.selectedDates.map(function (date) {
  600. return _this.formatDate(altFormat, date)
  601. });
  602. altValues = altValues.join(this.opts.multipleDatesSeparator);
  603. this.$altField.val(altValues);
  604. }
  605. value = value.join(this.opts.multipleDatesSeparator);
  606. this.$el.val(value)
  607. },
  608. /**
  609. * Check if date is between minDate and maxDate
  610. * @param date {object} - date object
  611. * @param type {string} - cell type
  612. * @returns {boolean}
  613. * @private
  614. */
  615. _isInRange: function (date, type) {
  616. var time = date.getTime(),
  617. d = datepicker.getParsedDate(date),
  618. min = datepicker.getParsedDate(this.minDate),
  619. max = datepicker.getParsedDate(this.maxDate),
  620. dMinTime = new Date(d.year, d.month, min.date).getTime(),
  621. dMaxTime = new Date(d.year, d.month, max.date).getTime(),
  622. types = {
  623. day: time >= this.minTime && time <= this.maxTime,
  624. month: dMinTime >= this.minTime && dMaxTime <= this.maxTime,
  625. year: d.year >= min.year && d.year <= max.year
  626. };
  627. return type ? types[type] : types.day
  628. },
  629. _getDimensions: function ($el) {
  630. var offset = $el.offset();
  631. return {
  632. width: $el.outerWidth(),
  633. height: $el.outerHeight(),
  634. left: offset.left,
  635. top: offset.top
  636. }
  637. },
  638. _getDateFromCell: function (cell) {
  639. var curDate = this.parsedDate,
  640. year = cell.data('year') || curDate.year,
  641. month = cell.data('month') == undefined ? curDate.month : cell.data('month'),
  642. date = cell.data('date') || 1;
  643. return new Date(year, month, date);
  644. },
  645. _setPositionClasses: function (pos) {
  646. pos = pos.split(' ');
  647. var main = pos[0],
  648. sec = pos[1],
  649. classes = 'datepicker -' + main + '-' + sec + '- -from-' + main + '-';
  650. if (this.visible) classes += ' active';
  651. this.$datepicker
  652. .removeAttr('class')
  653. .addClass(classes);
  654. },
  655. setPosition: function (position) {
  656. position = position || this.opts.position;
  657. var dims = this._getDimensions(this.$el),
  658. selfDims = this._getDimensions(this.$datepicker),
  659. pos = position.split(' '),
  660. top, left,
  661. offset = this.opts.offset,
  662. main = pos[0],
  663. secondary = pos[1];
  664. switch (main) {
  665. case 'top':
  666. top = dims.top - selfDims.height - offset;
  667. break;
  668. case 'right':
  669. left = dims.left + dims.width + offset;
  670. break;
  671. case 'bottom':
  672. top = dims.top + dims.height + offset;
  673. break;
  674. case 'left':
  675. left = dims.left - selfDims.width - offset;
  676. break;
  677. }
  678. switch(secondary) {
  679. case 'top':
  680. top = dims.top;
  681. break;
  682. case 'right':
  683. left = dims.left + dims.width - selfDims.width;
  684. break;
  685. case 'bottom':
  686. top = dims.top + dims.height - selfDims.height;
  687. break;
  688. case 'left':
  689. left = dims.left;
  690. break;
  691. case 'center':
  692. if (/left|right/.test(main)) {
  693. top = dims.top + dims.height/2 - selfDims.height/2;
  694. } else {
  695. left = dims.left + dims.width/2 - selfDims.width/2;
  696. }
  697. }
  698. this.$datepicker
  699. .css({
  700. left: left,
  701. top: top
  702. })
  703. },
  704. show: function () {
  705. var onShow = this.opts.onShow;
  706. this.setPosition(this.opts.position);
  707. this.$datepicker.addClass('active');
  708. this.visible = true;
  709. if (onShow) {
  710. this._bindVisionEvents(onShow)
  711. }
  712. },
  713. hide: function () {
  714. var onHide = this.opts.onHide;
  715. this.$datepicker
  716. .removeClass('active')
  717. .css({
  718. left: '-100000px'
  719. });
  720. this.focused = '';
  721. this.keys = [];
  722. this.inFocus = false;
  723. this.visible = false;
  724. this.$el.blur();
  725. if (onHide) {
  726. this._bindVisionEvents(onHide)
  727. }
  728. },
  729. down: function (date) {
  730. this._changeView(date, 'down');
  731. },
  732. up: function (date) {
  733. this._changeView(date, 'up');
  734. },
  735. _bindVisionEvents: function (event) {
  736. this.$datepicker.off('transitionend.dp');
  737. event(this, false);
  738. this.$datepicker.one('transitionend.dp', event.bind(this, this, true))
  739. },
  740. _changeView: function (date, dir) {
  741. date = date || this.focused || this.date;
  742. var nextView = dir == 'up' ? this.viewIndex + 1 : this.viewIndex - 1;
  743. if (nextView > 2) nextView = 2;
  744. if (nextView < 0) nextView = 0;
  745. this.silent = true;
  746. this.date = new Date(date.getFullYear(), date.getMonth(), 1);
  747. this.silent = false;
  748. this.view = this.viewIndexes[nextView];
  749. },
  750. _handleHotKey: function (key) {
  751. var date = datepicker.getParsedDate(this._getFocusedDate()),
  752. focusedParsed,
  753. o = this.opts,
  754. newDate,
  755. totalDaysInNextMonth,
  756. monthChanged = false,
  757. yearChanged = false,
  758. decadeChanged = false,
  759. y = date.year,
  760. m = date.month,
  761. d = date.date;
  762. switch (key) {
  763. case 'ctrlRight':
  764. case 'ctrlUp':
  765. m += 1;
  766. monthChanged = true;
  767. break;
  768. case 'ctrlLeft':
  769. case 'ctrlDown':
  770. m -= 1;
  771. monthChanged = true;
  772. break;
  773. case 'shiftRight':
  774. case 'shiftUp':
  775. yearChanged = true;
  776. y += 1;
  777. break;
  778. case 'shiftLeft':
  779. case 'shiftDown':
  780. yearChanged = true;
  781. y -= 1;
  782. break;
  783. case 'altRight':
  784. case 'altUp':
  785. decadeChanged = true;
  786. y += 10;
  787. break;
  788. case 'altLeft':
  789. case 'altDown':
  790. decadeChanged = true;
  791. y -= 10;
  792. break;
  793. case 'ctrlShiftUp':
  794. this.up();
  795. break;
  796. }
  797. totalDaysInNextMonth = datepicker.getDaysCount(new Date(y,m));
  798. newDate = new Date(y,m,d);
  799. // If next month has less days than current, set date to total days in that month
  800. if (totalDaysInNextMonth < d) d = totalDaysInNextMonth;
  801. // Check if newDate is in valid range
  802. if (newDate.getTime() < this.minTime) {
  803. newDate = this.minDate;
  804. } else if (newDate.getTime() > this.maxTime) {
  805. newDate = this.maxDate;
  806. }
  807. this.focused = newDate;
  808. focusedParsed = datepicker.getParsedDate(newDate);
  809. if (monthChanged && o.onChangeMonth) {
  810. o.onChangeMonth(focusedParsed.month, focusedParsed.year)
  811. }
  812. if (yearChanged && o.onChangeYear) {
  813. o.onChangeYear(focusedParsed.year)
  814. }
  815. if (decadeChanged && o.onChangeDecade) {
  816. o.onChangeDecade(this.curDecade)
  817. }
  818. },
  819. _registerKey: function (key) {
  820. var exists = this.keys.some(function (curKey) {
  821. return curKey == key;
  822. });
  823. if (!exists) {
  824. this.keys.push(key)
  825. }
  826. },
  827. _unRegisterKey: function (key) {
  828. var index = this.keys.indexOf(key);
  829. this.keys.splice(index, 1);
  830. },
  831. _isHotKeyPressed: function () {
  832. var currentHotKey,
  833. found = false,
  834. _this = this,
  835. pressedKeys = this.keys.sort();
  836. for (var hotKey in hotKeys) {
  837. currentHotKey = hotKeys[hotKey];
  838. if (pressedKeys.length != currentHotKey.length) continue;
  839. if (currentHotKey.every(function (key, i) { return key == pressedKeys[i]})) {
  840. _this._trigger('hotKey', hotKey);
  841. found = true;
  842. }
  843. }
  844. return found;
  845. },
  846. _trigger: function (event, args) {
  847. this.$el.trigger(event, args)
  848. },
  849. _focusNextCell: function (keyCode, type) {
  850. type = type || this.cellType;
  851. var date = datepicker.getParsedDate(this._getFocusedDate()),
  852. y = date.year,
  853. m = date.month,
  854. d = date.date;
  855. if (this._isHotKeyPressed()){
  856. return;
  857. }
  858. switch(keyCode) {
  859. case 37: // left
  860. type == 'day' ? (d -= 1) : '';
  861. type == 'month' ? (m -= 1) : '';
  862. type == 'year' ? (y -= 1) : '';
  863. break;
  864. case 38: // up
  865. type == 'day' ? (d -= 7) : '';
  866. type == 'month' ? (m -= 3) : '';
  867. type == 'year' ? (y -= 4) : '';
  868. break;
  869. case 39: // right
  870. type == 'day' ? (d += 1) : '';
  871. type == 'month' ? (m += 1) : '';
  872. type == 'year' ? (y += 1) : '';
  873. break;
  874. case 40: // down
  875. type == 'day' ? (d += 7) : '';
  876. type == 'month' ? (m += 3) : '';
  877. type == 'year' ? (y += 4) : '';
  878. break;
  879. }
  880. var nd = new Date(y,m,d);
  881. if (nd.getTime() < this.minTime) {
  882. nd = this.minDate;
  883. } else if (nd.getTime() > this.maxTime) {
  884. nd = this.maxDate;
  885. }
  886. this.focused = nd;
  887. },
  888. _getFocusedDate: function () {
  889. var focused = this.focused || this.selectedDates[this.selectedDates.length - 1],
  890. d = this.parsedDate;
  891. if (!focused) {
  892. switch (this.view) {
  893. case 'days':
  894. focused = new Date(d.year, d.month, new Date().getDate());
  895. break;
  896. case 'months':
  897. focused = new Date(d.year, d.month, 1);
  898. break;
  899. case 'years':
  900. focused = new Date(d.year, 0, 1);
  901. break;
  902. }
  903. }
  904. return focused;
  905. },
  906. _getCell: function (date, type) {
  907. type = type || this.cellType;
  908. var d = datepicker.getParsedDate(date),
  909. selector = '.datepicker--cell[data-year="' + d.year + '"]',
  910. $cell;
  911. switch (type) {
  912. case 'month':
  913. selector = '[data-month="' + d.month + '"]';
  914. break;
  915. case 'day':
  916. selector += '[data-month="' + d.month + '"][data-date="' + d.date + '"]';
  917. break;
  918. }
  919. $cell = this.views[this.currentView].$el.find(selector);
  920. return $cell.length ? $cell : $('');
  921. },
  922. destroy: function () {
  923. var _this = this;
  924. _this.$el
  925. .off('.adp')
  926. .data('datepicker', '');
  927. _this.selectedDates = [];
  928. _this.focused = '';
  929. _this.views = {};
  930. _this.keys = [];
  931. _this.minRange = '';
  932. _this.maxRange = '';
  933. if (_this.opts.inline || !_this.elIsInput) {
  934. _this.$datepicker.closest('.datepicker-inline').remove();
  935. } else {
  936. _this.$datepicker.remove();
  937. }
  938. },
  939. _handleAlreadySelectedDates: function (alreadySelected, selectedDate) {
  940. if (this.opts.range) {
  941. if (!this.opts.toggleSelected) {
  942. // Add possibility to select same date when range is true
  943. if (this.selectedDates.length != 2) {
  944. this._trigger('clickCell', selectedDate);
  945. }
  946. } else {
  947. this.removeDate(selectedDate);
  948. }
  949. } else if (this.opts.toggleSelected){
  950. this.removeDate(selectedDate);
  951. }
  952. // Change last selected date to be able to change time when clicking on this cell
  953. if (!this.opts.toggleSelected) {
  954. this.lastSelectedDate = alreadySelected;
  955. if (this.opts.timepicker) {
  956. this.timepicker._setTime(alreadySelected);
  957. this.timepicker.update();
  958. }
  959. }
  960. },
  961. _onShowEvent: function (e) {
  962. if (!this.visible) {
  963. this.show();
  964. }
  965. },
  966. _onBlur: function () {
  967. if (!this.inFocus && this.visible) {
  968. this.hide();
  969. }
  970. },
  971. _onMouseDownDatepicker: function (e) {
  972. this.inFocus = true;
  973. },
  974. _onMouseUpDatepicker: function (e) {
  975. this.inFocus = false;
  976. e.originalEvent.inFocus = true;
  977. if (!e.originalEvent.timepickerFocus) this.$el.focus();
  978. },
  979. _onKeyUpGeneral: function (e) {
  980. var val = this.$el.val();
  981. if (!val) {
  982. this.clear();
  983. }
  984. },
  985. _onResize: function () {
  986. if (this.visible) {
  987. this.setPosition();
  988. }
  989. },
  990. _onMouseUpBody: function (e) {
  991. if (e.originalEvent.inFocus) return;
  992. if (this.visible && !this.inFocus) {
  993. this.hide();
  994. }
  995. },
  996. _onMouseUpEl: function (e) {
  997. e.originalEvent.inFocus = true;
  998. setTimeout(this._onKeyUpGeneral.bind(this),4);
  999. },
  1000. _onKeyDown: function (e) {
  1001. var code = e.which;
  1002. this._registerKey(code);
  1003. // Arrows
  1004. if (code >= 37 && code <= 40) {
  1005. e.preventDefault();
  1006. this._focusNextCell(code);
  1007. }
  1008. // Enter
  1009. if (code == 13) {
  1010. if (this.focused) {
  1011. if (this._getCell(this.focused).hasClass('-disabled-')) return;
  1012. if (this.view != this.opts.minView) {
  1013. this.down()
  1014. } else {
  1015. var alreadySelected = this._isSelected(this.focused, this.cellType);
  1016. if (!alreadySelected) {
  1017. if (this.timepicker) {
  1018. this.focused.setHours(this.timepicker.hours);
  1019. this.focused.setMinutes(this.timepicker.minutes);
  1020. this.focused.setSeconds(this.timepicker.seconds);
  1021. }
  1022. this.selectDate(this.focused);
  1023. return;
  1024. }
  1025. this._handleAlreadySelectedDates(alreadySelected, this.focused)
  1026. }
  1027. }
  1028. }
  1029. // Esc
  1030. if (code == 27) {
  1031. this.hide();
  1032. }
  1033. },
  1034. _onKeyUp: function (e) {
  1035. var code = e.which;
  1036. this._unRegisterKey(code);
  1037. },
  1038. _onHotKey: function (e, hotKey) {
  1039. this._handleHotKey(hotKey);
  1040. },
  1041. _onMouseEnterCell: function (e) {
  1042. var $cell = $(e.target).closest('.datepicker--cell'),
  1043. date = this._getDateFromCell($cell);
  1044. // Prevent from unnecessary rendering and setting new currentDate
  1045. this.silent = true;
  1046. if (this.focused) {
  1047. this.focused = ''
  1048. }
  1049. $cell.addClass('-focus-');
  1050. this.focused = date;
  1051. this.silent = false;
  1052. if (this.opts.range && this.selectedDates.length == 1) {
  1053. this.minRange = this.selectedDates[0];
  1054. this.maxRange = '';
  1055. if (datepicker.less(this.minRange, this.focused)) {
  1056. this.maxRange = this.minRange;
  1057. this.minRange = '';
  1058. }
  1059. this.views[this.currentView]._update();
  1060. }
  1061. },
  1062. _onMouseLeaveCell: function (e) {
  1063. var $cell = $(e.target).closest('.datepicker--cell');
  1064. $cell.removeClass('-focus-');
  1065. this.silent = true;
  1066. this.focused = '';
  1067. this.silent = false;
  1068. },
  1069. _onTimeChange: function (e, h, m, s) {
  1070. var date = new Date(),
  1071. selectedDates = this.selectedDates,
  1072. selected = false;
  1073. if (selectedDates.length) {
  1074. selected = true;
  1075. date = this.lastSelectedDate;
  1076. }
  1077. date.setHours(h);
  1078. date.setMinutes(m);
  1079. date.setSeconds(s);
  1080. if (!selected && !this._getCell(date).hasClass('-disabled-')) {
  1081. this.selectDate(date);
  1082. } else {
  1083. this._setInputValue();
  1084. if (this.opts.onSelect) {
  1085. this._triggerOnChange();
  1086. }
  1087. }
  1088. },
  1089. _onClickCell: function (e, date) {
  1090. if (this.timepicker) {
  1091. date.setHours(this.timepicker.hours);
  1092. date.setMinutes(this.timepicker.minutes);
  1093. date.setSeconds(this.timepicker.seconds)
  1094. }
  1095. this.selectDate(date);
  1096. },
  1097. set focused(val) {
  1098. if (!val && this.focused) {
  1099. var $cell = this._getCell(this.focused);
  1100. if ($cell.length) {
  1101. $cell.removeClass('-focus-')
  1102. }
  1103. }
  1104. this._focused = val;
  1105. if (this.opts.range && this.selectedDates.length == 1) {
  1106. this.minRange = this.selectedDates[0];
  1107. this.maxRange = '';
  1108. if (datepicker.less(this.minRange, this._focused)) {
  1109. this.maxRange = this.minRange;
  1110. this.minRange = '';
  1111. }
  1112. }
  1113. if (this.silent) return;
  1114. this.date = val;
  1115. },
  1116. get focused() {
  1117. return this._focused;
  1118. },
  1119. get parsedDate() {
  1120. return datepicker.getParsedDate(this.date);
  1121. },
  1122. set date (val) {
  1123. if (!(val instanceof Date)) return;
  1124. this.currentDate = val;
  1125. if (this.inited && !this.silent) {
  1126. this.views[this.view]._render();
  1127. this.nav._render();
  1128. if (this.visible && this.elIsInput) {
  1129. this.setPosition();
  1130. }
  1131. }
  1132. return val;
  1133. },
  1134. get date () {
  1135. return this.currentDate
  1136. },
  1137. set view (val) {
  1138. this.viewIndex = this.viewIndexes.indexOf(val);
  1139. if (this.viewIndex < 0) {
  1140. return;
  1141. }
  1142. this.prevView = this.currentView;
  1143. this.currentView = val;
  1144. if (this.inited) {
  1145. if (!this.views[val]) {
  1146. this.views[val] = new $.fn.datepicker.Body(this, val, this.opts)
  1147. } else {
  1148. this.views[val]._render();
  1149. }
  1150. this.views[this.prevView].hide();
  1151. this.views[val].show();
  1152. this.nav._render();
  1153. if (this.opts.onChangeView) {
  1154. this.opts.onChangeView(val)
  1155. }
  1156. if (this.elIsInput && this.visible) this.setPosition();
  1157. }
  1158. return val
  1159. },
  1160. get view() {
  1161. return this.currentView;
  1162. },
  1163. get cellType() {
  1164. return this.view.substring(0, this.view.length - 1)
  1165. },
  1166. get minTime() {
  1167. var min = datepicker.getParsedDate(this.minDate);
  1168. return new Date(min.year, min.month, min.date).getTime()
  1169. },
  1170. get maxTime() {
  1171. var max = datepicker.getParsedDate(this.maxDate);
  1172. return new Date(max.year, max.month, max.date).getTime()
  1173. },
  1174. get curDecade() {
  1175. return datepicker.getDecade(this.date)
  1176. }
  1177. };
  1178. // Utils
  1179. // -------------------------------------------------
  1180. datepicker.getDaysCount = function (date) {
  1181. return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
  1182. };
  1183. datepicker.getParsedDate = function (date) {
  1184. return {
  1185. year: date.getFullYear(),
  1186. month: date.getMonth(),
  1187. fullMonth: (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1, // One based
  1188. date: date.getDate(),
  1189. fullDate: date.getDate() < 10 ? '0' + date.getDate() : date.getDate(),
  1190. day: date.getDay(),
  1191. hours: date.getHours(),
  1192. fullHours: date.getHours() < 10 ? '0' + date.getHours() : date.getHours() ,
  1193. minutes: date.getMinutes(),
  1194. fullMinutes: date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(),
  1195. seconds: date.getSeconds(),
  1196. fullSeconds: date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
  1197. }
  1198. };
  1199. datepicker.getDecade = function (date) {
  1200. var firstYear = Math.floor(date.getFullYear() / 10) * 10;
  1201. return [firstYear, firstYear + 9];
  1202. };
  1203. datepicker.template = function (str, data) {
  1204. return str.replace(/#\{([\w]+)\}/g, function (source, match) {
  1205. if (data[match] || data[match] === 0) {
  1206. return data[match]
  1207. }
  1208. });
  1209. };
  1210. datepicker.isSame = function (date1, date2, type) {
  1211. if (!date1 || !date2) return false;
  1212. var d1 = datepicker.getParsedDate(date1),
  1213. d2 = datepicker.getParsedDate(date2),
  1214. _type = type ? type : 'day',
  1215. conditions = {
  1216. day: d1.date == d2.date && d1.month == d2.month && d1.year == d2.year,
  1217. month: d1.month == d2.month && d1.year == d2.year,
  1218. year: d1.year == d2.year
  1219. };
  1220. return conditions[_type];
  1221. };
  1222. datepicker.less = function (dateCompareTo, date, type) {
  1223. if (!dateCompareTo || !date) return false;
  1224. return date.getTime() < dateCompareTo.getTime();
  1225. };
  1226. datepicker.bigger = function (dateCompareTo, date, type) {
  1227. if (!dateCompareTo || !date) return false;
  1228. return date.getTime() > dateCompareTo.getTime();
  1229. };
  1230. datepicker.getLeadingZeroNum = function (num) {
  1231. return parseInt(num) < 10 ? '0' + num : num;
  1232. };
  1233. /**
  1234. * Returns copy of date with hours and minutes equals to 0
  1235. * @param date {Date}
  1236. */
  1237. datepicker.resetTime = function (date) {
  1238. if (typeof date != 'object') return;
  1239. date = datepicker.getParsedDate(date);
  1240. return new Date(date.year, date.month, date.date)
  1241. };
  1242. $.fn.datepicker = function ( options ) {
  1243. return this.each(function () {
  1244. if (!$.data(this, pluginName)) {
  1245. $.data(this, pluginName,
  1246. new Datepicker( this, options ));
  1247. } else {
  1248. var _this = $.data(this, pluginName);
  1249. _this.opts = $.extend(true, _this.opts, options);
  1250. _this.update();
  1251. }
  1252. });
  1253. };
  1254. $.fn.datepicker.Constructor = Datepicker;
  1255. $.fn.datepicker.language = {
  1256. ru: {
  1257. days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
  1258. daysShort: ['Вос','Пон','Вто','Сре','Чет','Пят','Суб'],
  1259. daysMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
  1260. months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
  1261. monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
  1262. today: 'Сегодня',
  1263. clear: 'Очистить',
  1264. dateFormat: 'dd.mm.yyyy',
  1265. timeFormat: 'hh:ii',
  1266. firstDay: 1
  1267. }
  1268. };
  1269. $(function () {
  1270. $(autoInitSelector).datepicker();
  1271. })
  1272. })();
  1273. ;(function () {
  1274. var templates = {
  1275. days:'' +
  1276. '<div class="datepicker--days datepicker--body">' +
  1277. '<div class="datepicker--days-names"></div>' +
  1278. '<div class="datepicker--cells datepicker--cells-days"></div>' +
  1279. '</div>',
  1280. months: '' +
  1281. '<div class="datepicker--months datepicker--body">' +
  1282. '<div class="datepicker--cells datepicker--cells-months"></div>' +
  1283. '</div>',
  1284. years: '' +
  1285. '<div class="datepicker--years datepicker--body">' +
  1286. '<div class="datepicker--cells datepicker--cells-years"></div>' +
  1287. '</div>'
  1288. },
  1289. datepicker = $.fn.datepicker,
  1290. dp = datepicker.Constructor;
  1291. datepicker.Body = function (d, type, opts) {
  1292. this.d = d;
  1293. this.type = type;
  1294. this.opts = opts;
  1295. this.$el = $('');
  1296. if (this.opts.onlyTimepicker) return;
  1297. this.init();
  1298. };
  1299. datepicker.Body.prototype = {
  1300. init: function () {
  1301. this._buildBaseHtml();
  1302. this._render();
  1303. this._bindEvents();
  1304. },
  1305. _bindEvents: function () {
  1306. this.$el.on('click', '.datepicker--cell', $.proxy(this._onClickCell, this));
  1307. },
  1308. _buildBaseHtml: function () {
  1309. this.$el = $(templates[this.type]).appendTo(this.d.$content);
  1310. this.$names = $('.datepicker--days-names', this.$el);
  1311. this.$cells = $('.datepicker--cells', this.$el);
  1312. },
  1313. _getDayNamesHtml: function (firstDay, curDay, html, i) {
  1314. curDay = curDay != undefined ? curDay : firstDay;
  1315. html = html ? html : '';
  1316. i = i != undefined ? i : 0;
  1317. if (i > 7) return html;
  1318. if (curDay == 7) return this._getDayNamesHtml(firstDay, 0, html, ++i);
  1319. html += '<div class="datepicker--day-name' + (this.d.isWeekend(curDay) ? " -weekend-" : "") + '">' + this.d.loc.daysMin[curDay] + '</div>';
  1320. return this._getDayNamesHtml(firstDay, ++curDay, html, ++i);
  1321. },
  1322. _getCellContents: function (date, type) {
  1323. var classes = "datepicker--cell datepicker--cell-" + type,
  1324. currentDate = new Date(),
  1325. parent = this.d,
  1326. minRange = dp.resetTime(parent.minRange),
  1327. maxRange = dp.resetTime(parent.maxRange),
  1328. opts = parent.opts,
  1329. d = dp.getParsedDate(date),
  1330. render = {},
  1331. html = d.date;
  1332. switch (type) {
  1333. case 'day':
  1334. if (parent.isWeekend(d.day)) classes += " -weekend-";
  1335. if (d.month != this.d.parsedDate.month) {
  1336. classes += " -other-month-";
  1337. if (!opts.selectOtherMonths) {
  1338. classes += " -disabled-";
  1339. }
  1340. if (!opts.showOtherMonths) html = '';
  1341. }
  1342. break;
  1343. case 'month':
  1344. html = parent.loc[parent.opts.monthsField][d.month];
  1345. break;
  1346. case 'year':
  1347. var decade = parent.curDecade;
  1348. html = d.year;
  1349. if (d.year < decade[0] || d.year > decade[1]) {
  1350. classes += ' -other-decade-';
  1351. if (!opts.selectOtherYears) {
  1352. classes += " -disabled-";
  1353. }
  1354. if (!opts.showOtherYears) html = '';
  1355. }
  1356. break;
  1357. }
  1358. if (opts.onRenderCell) {
  1359. render = opts.onRenderCell(date, type) || {};
  1360. html = render.html ? render.html : html;
  1361. classes += render.classes ? ' ' + render.classes : '';
  1362. }
  1363. if (opts.range) {
  1364. if (dp.isSame(minRange, date, type)) classes += ' -range-from-';
  1365. if (dp.isSame(maxRange, date, type)) classes += ' -range-to-';
  1366. if (parent.selectedDates.length == 1 && parent.focused) {
  1367. if (
  1368. (dp.bigger(minRange, date) && dp.less(parent.focused, date)) ||
  1369. (dp.less(maxRange, date) && dp.bigger(parent.focused, date)))
  1370. {
  1371. classes += ' -in-range-'
  1372. }
  1373. if (dp.less(maxRange, date) && dp.isSame(parent.focused, date)) {
  1374. classes += ' -range-from-'
  1375. }
  1376. if (dp.bigger(minRange, date) && dp.isSame(parent.focused, date)) {
  1377. classes += ' -range-to-'
  1378. }
  1379. } else if (parent.selectedDates.length == 2) {
  1380. if (dp.bigger(minRange, date) && dp.less(maxRange, date)) {
  1381. classes += ' -in-range-'
  1382. }
  1383. }
  1384. }
  1385. if (dp.isSame(currentDate, date, type)) classes += ' -current-';
  1386. if (parent.focused && dp.isSame(date, parent.focused, type)) classes += ' -focus-';
  1387. if (parent._isSelected(date, type)) classes += ' -selected-';
  1388. if (!parent._isInRange(date, type) || render.disabled) classes += ' -disabled-';
  1389. return {
  1390. html: html,
  1391. classes: classes
  1392. }
  1393. },
  1394. /**
  1395. * Calculates days number to render. Generates days html and returns it.
  1396. * @param {object} date - Date object
  1397. * @returns {string}
  1398. * @private
  1399. */
  1400. _getDaysHtml: function (date) {
  1401. var totalMonthDays = dp.getDaysCount(date),
  1402. firstMonthDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(),
  1403. lastMonthDay = new Date(date.getFullYear(), date.getMonth(), totalMonthDays).getDay(),
  1404. daysFromPevMonth = firstMonthDay - this.d.loc.firstDay,
  1405. daysFromNextMonth = 6 - lastMonthDay + this.d.loc.firstDay;
  1406. daysFromPevMonth = daysFromPevMonth < 0 ? daysFromPevMonth + 7 : daysFromPevMonth;
  1407. daysFromNextMonth = daysFromNextMonth > 6 ? daysFromNextMonth - 7 : daysFromNextMonth;
  1408. var startDayIndex = -daysFromPevMonth + 1,
  1409. m, y,
  1410. html = '';
  1411. for (var i = startDayIndex, max = totalMonthDays + daysFromNextMonth; i <= max; i++) {
  1412. y = date.getFullYear();
  1413. m = date.getMonth();
  1414. html += this._getDayHtml(new Date(y, m, i))
  1415. }
  1416. return html;
  1417. },
  1418. _getDayHtml: function (date) {
  1419. var content = this._getCellContents(date, 'day');
  1420. return '<div class="' + content.classes + '" ' +
  1421. 'data-date="' + date.getDate() + '" ' +
  1422. 'data-month="' + date.getMonth() + '" ' +
  1423. 'data-year="' + date.getFullYear() + '">' + content.html + '</div>';
  1424. },
  1425. /**
  1426. * Generates months html
  1427. * @param {object} date - date instance
  1428. * @returns {string}
  1429. * @private
  1430. */
  1431. _getMonthsHtml: function (date) {
  1432. var html = '',
  1433. d = dp.getParsedDate(date),
  1434. i = 0;
  1435. while(i < 12) {
  1436. html += this._getMonthHtml(new Date(d.year, i));
  1437. i++
  1438. }
  1439. return html;
  1440. },
  1441. _getMonthHtml: function (date) {
  1442. var content = this._getCellContents(date, 'month');
  1443. return '<div class="' + content.classes + '" data-month="' + date.getMonth() + '">' + content.html + '</div>'
  1444. },
  1445. _getYearsHtml: function (date) {
  1446. var d = dp.getParsedDate(date),
  1447. decade = dp.getDecade(date),
  1448. firstYear = decade[0] - 1,
  1449. html = '',
  1450. i = firstYear;
  1451. for (i; i <= decade[1] + 1; i++) {
  1452. html += this._getYearHtml(new Date(i , 0));
  1453. }
  1454. return html;
  1455. },
  1456. _getYearHtml: function (date) {
  1457. var content = this._getCellContents(date, 'year');
  1458. return '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + content.html + '</div>'
  1459. },
  1460. _renderTypes: {
  1461. days: function () {
  1462. var dayNames = this._getDayNamesHtml(this.d.loc.firstDay),
  1463. days = this._getDaysHtml(this.d.currentDate);
  1464. this.$cells.html(days);
  1465. this.$names.html(dayNames)
  1466. },
  1467. months: function () {
  1468. var html = this._getMonthsHtml(this.d.currentDate);
  1469. this.$cells.html(html)
  1470. },
  1471. years: function () {
  1472. var html = this._getYearsHtml(this.d.currentDate);
  1473. this.$cells.html(html)
  1474. }
  1475. },
  1476. _render: function () {
  1477. if (this.opts.onlyTimepicker) return;
  1478. this._renderTypes[this.type].bind(this)();
  1479. },
  1480. _update: function () {
  1481. var $cells = $('.datepicker--cell', this.$cells),
  1482. _this = this,
  1483. classes,
  1484. $cell,
  1485. date;
  1486. $cells.each(function (cell, i) {
  1487. $cell = $(this);
  1488. date = _this.d._getDateFromCell($(this));
  1489. classes = _this._getCellContents(date, _this.d.cellType);
  1490. $cell.attr('class',classes.classes)
  1491. });
  1492. },
  1493. show: function () {
  1494. if (this.opts.onlyTimepicker) return;
  1495. this.$el.addClass('active');
  1496. this.acitve = true;
  1497. },
  1498. hide: function () {
  1499. this.$el.removeClass('active');
  1500. this.active = false;
  1501. },
  1502. // Events
  1503. // -------------------------------------------------
  1504. _handleClick: function (el) {
  1505. var date = el.data('date') || 1,
  1506. month = el.data('month') || 0,
  1507. year = el.data('year') || this.d.parsedDate.year,
  1508. dp = this.d;
  1509. // Change view if min view does not reach yet
  1510. if (dp.view != this.opts.minView) {
  1511. dp.down(new Date(year, month, date));
  1512. return;
  1513. }
  1514. // Select date if min view is reached
  1515. var selectedDate = new Date(year, month, date),
  1516. alreadySelected = this.d._isSelected(selectedDate, this.d.cellType);
  1517. if (!alreadySelected) {
  1518. dp._trigger('clickCell', selectedDate);
  1519. return;
  1520. }
  1521. dp._handleAlreadySelectedDates.bind(dp, alreadySelected, selectedDate)();
  1522. },
  1523. _onClickCell: function (e) {
  1524. var $el = $(e.target).closest('.datepicker--cell');
  1525. if ($el.hasClass('-disabled-')) return;
  1526. this._handleClick.bind(this)($el);
  1527. }
  1528. };
  1529. })();
  1530. ;(function () {
  1531. var template = '' +
  1532. '<div class="datepicker--nav-action" data-action="prev">#{prevHtml}</div>' +
  1533. '<div class="datepicker--nav-title">#{title}</div>' +
  1534. '<div class="datepicker--nav-action" data-action="next">#{nextHtml}</div>',
  1535. buttonsContainerTemplate = '<div class="datepicker--buttons"></div>',
  1536. button = '<span class="datepicker--button" data-action="#{action}">#{label}</span>',
  1537. datepicker = $.fn.datepicker,
  1538. dp = datepicker.Constructor;
  1539. datepicker.Navigation = function (d, opts) {
  1540. this.d = d;
  1541. this.opts = opts;
  1542. this.$buttonsContainer = '';
  1543. this.init();
  1544. };
  1545. datepicker.Navigation.prototype = {
  1546. init: function () {
  1547. this._buildBaseHtml();
  1548. this._bindEvents();
  1549. },
  1550. _bindEvents: function () {
  1551. this.d.$nav.on('click', '.datepicker--nav-action', $.proxy(this._onClickNavButton, this));
  1552. this.d.$nav.on('click', '.datepicker--nav-title', $.proxy(this._onClickNavTitle, this));
  1553. this.d.$datepicker.on('click', '.datepicker--button', $.proxy(this._onClickNavButton, this));
  1554. },
  1555. _buildBaseHtml: function () {
  1556. if (!this.opts.onlyTimepicker) {
  1557. this._render();
  1558. }
  1559. this._addButtonsIfNeed();
  1560. },
  1561. _addButtonsIfNeed: function () {
  1562. if (this.opts.todayButton) {
  1563. this._addButton('today')
  1564. }
  1565. if (this.opts.clearButton) {
  1566. this._addButton('clear')
  1567. }
  1568. },
  1569. _render: function () {
  1570. var title = this._getTitle(this.d.currentDate),
  1571. html = dp.template(template, $.extend({title: title}, this.opts));
  1572. this.d.$nav.html(html);
  1573. if (this.d.view == 'years') {
  1574. $('.datepicker--nav-title', this.d.$nav).addClass('-disabled-');
  1575. }
  1576. this.setNavStatus();
  1577. },
  1578. _getTitle: function (date) {
  1579. return this.d.formatDate(this.opts.navTitles[this.d.view], date)
  1580. },
  1581. _addButton: function (type) {
  1582. if (!this.$buttonsContainer.length) {
  1583. this._addButtonsContainer();
  1584. }
  1585. var data = {
  1586. action: type,
  1587. label: this.d.loc[type]
  1588. },
  1589. html = dp.template(button, data);
  1590. if ($('[data-action=' + type + ']', this.$buttonsContainer).length) return;
  1591. this.$buttonsContainer.append(html);
  1592. },
  1593. _addButtonsContainer: function () {
  1594. this.d.$datepicker.append(buttonsContainerTemplate);
  1595. this.$buttonsContainer = $('.datepicker--buttons', this.d.$datepicker);
  1596. },
  1597. setNavStatus: function () {
  1598. if (!(this.opts.minDate || this.opts.maxDate) || !this.opts.disableNavWhenOutOfRange) return;
  1599. var date = this.d.parsedDate,
  1600. m = date.month,
  1601. y = date.year,
  1602. d = date.date;
  1603. switch (this.d.view) {
  1604. case 'days':
  1605. if (!this.d._isInRange(new Date(y, m-1, 1), 'month')) {
  1606. this._disableNav('prev')
  1607. }
  1608. if (!this.d._isInRange(new Date(y, m+1, 1), 'month')) {
  1609. this._disableNav('next')
  1610. }
  1611. break;
  1612. case 'months':
  1613. if (!this.d._isInRange(new Date(y-1, m, d), 'year')) {
  1614. this._disableNav('prev')
  1615. }
  1616. if (!this.d._isInRange(new Date(y+1, m, d), 'year')) {
  1617. this._disableNav('next')
  1618. }
  1619. break;
  1620. case 'years':
  1621. var decade = dp.getDecade(this.d.date);
  1622. if (!this.d._isInRange(new Date(decade[0] - 1, 0, 1), 'year')) {
  1623. this._disableNav('prev')
  1624. }
  1625. if (!this.d._isInRange(new Date(decade[1] + 1, 0, 1), 'year')) {
  1626. this._disableNav('next')
  1627. }
  1628. break;
  1629. }
  1630. },
  1631. _disableNav: function (nav) {
  1632. $('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-')
  1633. },
  1634. _activateNav: function (nav) {
  1635. $('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-')
  1636. },
  1637. _onClickNavButton: function (e) {
  1638. var $el = $(e.target).closest('[data-action]'),
  1639. action = $el.data('action');
  1640. this.d[action]();
  1641. },
  1642. _onClickNavTitle: function (e) {
  1643. if ($(e.target).hasClass('-disabled-')) return;
  1644. if (this.d.view == 'days') {
  1645. return this.d.view = 'months'
  1646. }
  1647. this.d.view = 'years';
  1648. }
  1649. }
  1650. })();
  1651. ;(function () {
  1652. var template = '<div class="datepicker--time">' +
  1653. '<div class="datepicker--time-current">' +
  1654. ' <span class="datepicker--time-current-hours">#{hourVisible}</span>' +
  1655. ' <span class="datepicker--time-current-colon">:</span>' +
  1656. ' <span class="datepicker--time-current-minutes">#{minValue}</span>' +
  1657. ' <span class="datepicker--time-current-colon">:</span>' +
  1658. ' <span class="datepicker--time-current-seconds">#{secValue}</span>' +
  1659. '</div>' +
  1660. '<div class="datepicker--time-sliders">' +
  1661. ' <div class="datepicker--time-row">' +
  1662. ' <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' +
  1663. ' </div>' +
  1664. ' <div class="datepicker--time-row">' +
  1665. ' <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' +
  1666. ' </div>' +
  1667. ' <div class="datepicker--time-row">' +
  1668. ' <input type="range" name="seconds" value="#{secValue}" min="#{secMin}" max="#{secMax}" step="#{secStep}"/>' +
  1669. ' </div>' +
  1670. '</div>' +
  1671. '</div>',
  1672. datepicker = $.fn.datepicker,
  1673. dp = datepicker.Constructor;
  1674. datepicker.Timepicker = function (inst, opts) {
  1675. this.d = inst;
  1676. this.opts = opts;
  1677. this.init();
  1678. };
  1679. datepicker.Timepicker.prototype = {
  1680. init: function () {
  1681. var input = 'input';
  1682. this._setTime(this.d.date);
  1683. this._buildHTML();
  1684. if (navigator.userAgent.match(/trident/gi)) {
  1685. input = 'change';
  1686. }
  1687. this.d.$el.on('selectDate', this._onSelectDate.bind(this));
  1688. this.$ranges.on(input, this._onChangeRange.bind(this));
  1689. this.$ranges.on('mouseup', this._onMouseUpRange.bind(this));
  1690. this.$ranges.on('mousemove focus ', this._onMouseEnterRange.bind(this));
  1691. this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this));
  1692. },
  1693. _setTime: function (date) {
  1694. var _date = dp.getParsedDate(date);
  1695. this._handleDate(date);
  1696. this.hours = _date.hours < this.minHours ? this.minHours : _date.hours;
  1697. this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes;
  1698. this.seconds = _date.seconds < this.minSeconds ? this.minSeconds : _date.seconds;
  1699. },
  1700. /**
  1701. * Sets minHours and minMinutes from date (usually it's a minDate)
  1702. * Also changes minMinutes if current hours are bigger then @date hours
  1703. * @param date {Date}
  1704. * @private
  1705. */
  1706. _setMinTimeFromDate: function (date) {
  1707. this.minHours = date.getHours();
  1708. this.minMinutes = date.getMinutes();
  1709. this.minSeconds = date.getSeconds();
  1710. // If, for example, min hours are 10, and current hours are 12,
  1711. // update minMinutes to default value, to be able to choose whole range of values
  1712. if (this.d.lastSelectedDate) {
  1713. if (this.d.lastSelectedDate.getHours() > date.getHours()) {
  1714. this.minMinutes = this.opts.minMinutes;
  1715. }
  1716. }
  1717. },
  1718. _setMaxTimeFromDate: function (date) {
  1719. this.maxHours = date.getHours();
  1720. this.maxMinutes = date.getMinutes();
  1721. this.maxSeconds = date.getSeconds();
  1722. if (this.d.lastSelectedDate) {
  1723. if (this.d.lastSelectedDate.getHours() < date.getHours()) {
  1724. this.maxMinutes = this.opts.maxMinutes;
  1725. }
  1726. }
  1727. },
  1728. _setDefaultMinMaxTime: function () {
  1729. var maxHours = 23,
  1730. maxMinutes = 59,
  1731. maxSeconds = 59,
  1732. opts = this.opts;
  1733. this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours;
  1734. this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes;
  1735. this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours;
  1736. this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes;
  1737. this.minSeconds = opts.minSeconds < 0 || opts.minSeconds > maxSeconds ? 0 : opts.minSeconds;
  1738. this.maxSeconds = opts.maxSeconds < 0 || opts.maxSeconds > maxSeconds ? maxSeconds : opts.maxSeconds;
  1739. },
  1740. /**
  1741. * Looks for min/max hours/minutes and if current values
  1742. * are out of range sets valid values.
  1743. * @private
  1744. */
  1745. _validateHoursMinutes: function (date) {
  1746. if (this.hours < this.minHours) {
  1747. this.hours = this.minHours;
  1748. } else if (this.hours > this.maxHours) {
  1749. this.hours = this.maxHours;
  1750. }
  1751. if (this.minutes < this.minMinutes) {
  1752. this.minutes = this.minMinutes;
  1753. } else if (this.minutes > this.maxMinutes) {
  1754. this.minutes = this.maxMinutes;
  1755. }
  1756. if (this.seconds < this.minSeconds) {
  1757. this.seconds = this.minSeconds;
  1758. } else if (this.seconds > this.maxSeconds) {
  1759. this.seconds = this.maxSeconds;
  1760. }
  1761. },
  1762. _buildHTML: function () {
  1763. var lz = dp.getLeadingZeroNum,
  1764. data = {
  1765. hourMin: this.minHours,
  1766. hourMax: lz(this.maxHours),
  1767. hourStep: this.opts.hoursStep,
  1768. hourValue: this.hours,
  1769. hourVisible: lz(this.displayHours),
  1770. minMin: this.minMinutes,
  1771. minMax: lz(this.maxMinutes),
  1772. minStep: this.opts.minutesStep,
  1773. minValue: lz(this.minutes),
  1774. secMin: this.minSeconds,
  1775. secMax: lz(this.maxSeconds),
  1776. secStep: this.opts.secondsStep,
  1777. secValue: lz(this.seconds)
  1778. },
  1779. _template = dp.template(template, data);
  1780. this.$timepicker = $(_template).appendTo(this.d.$datepicker);
  1781. this.$ranges = $('[type="range"]', this.$timepicker);
  1782. this.$hours = $('[name="hours"]', this.$timepicker);
  1783. this.$minutes = $('[name="minutes"]', this.$timepicker);
  1784. this.$seconds = $('[name="seconds"]', this.$timepicker);
  1785. this.$hoursText = $('.datepicker--time-current-hours', this.$timepicker);
  1786. this.$minutesText = $('.datepicker--time-current-minutes', this.$timepicker);
  1787. this.$secondsText = $('.datepicker--time-current-seconds', this.$timepicker);
  1788. if (this.d.ampm) {
  1789. this.$ampm = $('<span class="datepicker--time-current-ampm">')
  1790. .appendTo($('.datepicker--time-current', this.$timepicker))
  1791. .html(this.dayPeriod);
  1792. this.$timepicker.addClass('-am-pm-');
  1793. }
  1794. },
  1795. _updateCurrentTime: function () {
  1796. var h = dp.getLeadingZeroNum(this.displayHours),
  1797. m = dp.getLeadingZeroNum(this.minutes),
  1798. s = dp.getLeadingZeroNum(this.seconds);
  1799. this.$hoursText.html(h);
  1800. this.$minutesText.html(m);
  1801. this.$secondsText.html(s);
  1802. if (this.d.ampm) {
  1803. this.$ampm.html(this.dayPeriod);
  1804. }
  1805. },
  1806. _updateRanges: function () {
  1807. this.$hours.attr({
  1808. min: this.minHours,
  1809. max: this.maxHours
  1810. }).val(this.hours);
  1811. this.$minutes.attr({
  1812. min: this.minMinutes,
  1813. max: this.maxMinutes
  1814. }).val(this.minutes);
  1815. this.$seconds.attr({
  1816. min: this.minSeconds,
  1817. max: this.maxSeconds
  1818. }).val(this.seconds);
  1819. },
  1820. /**
  1821. * Sets minHours, minMinutes etc. from date. If date is not passed, than sets
  1822. * values from options
  1823. * @param [date] {object} - Date object, to get values from
  1824. * @private
  1825. */
  1826. _handleDate: function (date) {
  1827. this._setDefaultMinMaxTime();
  1828. if (date) {
  1829. if (dp.isSame(date, this.d.opts.minDate)) {
  1830. this._setMinTimeFromDate(this.d.opts.minDate);
  1831. } else if (dp.isSame(date, this.d.opts.maxDate)) {
  1832. this._setMaxTimeFromDate(this.d.opts.maxDate);
  1833. }
  1834. }
  1835. this._validateHoursMinutes(date);
  1836. },
  1837. update: function () {
  1838. this._updateRanges();
  1839. this._updateCurrentTime();
  1840. },
  1841. /**
  1842. * Calculates valid hour value to display in text input and datepicker's body.
  1843. * @param date {Date|Number} - date or hours
  1844. * @param [ampm] {Boolean} - 12 hours mode
  1845. * @returns {{hours: *, dayPeriod: string}}
  1846. * @private
  1847. */
  1848. _getValidHoursFromDate: function (date, ampm) {
  1849. var d = date,
  1850. hours = date;
  1851. if (date instanceof Date) {
  1852. d = dp.getParsedDate(date);
  1853. hours = d.hours;
  1854. }
  1855. var _ampm = ampm || this.d.ampm,
  1856. dayPeriod = 'am';
  1857. if (_ampm) {
  1858. switch(true) {
  1859. case hours == 0:
  1860. hours = 12;
  1861. break;
  1862. case hours == 12:
  1863. dayPeriod = 'pm';
  1864. break;
  1865. case hours > 11:
  1866. hours = hours - 12;
  1867. dayPeriod = 'pm';
  1868. break;
  1869. default:
  1870. break;
  1871. }
  1872. }
  1873. return {
  1874. hours: hours,
  1875. dayPeriod: dayPeriod
  1876. }
  1877. },
  1878. set hours (val) {
  1879. this._hours = val;
  1880. var displayHours = this._getValidHoursFromDate(val);
  1881. this.displayHours = displayHours.hours;
  1882. this.dayPeriod = displayHours.dayPeriod;
  1883. },
  1884. get hours() {
  1885. return this._hours;
  1886. },
  1887. // Events
  1888. // -------------------------------------------------
  1889. _onChangeRange: function (e) {
  1890. var $target = $(e.target),
  1891. name = $target.attr('name');
  1892. this.d.timepickerIsActive = true;
  1893. this[name] = $target.val();
  1894. this._updateCurrentTime();
  1895. this.d._trigger('timeChange', [this.hours, this.minutes, this.seconds]);
  1896. this._handleDate(this.d.lastSelectedDate);
  1897. this.update()
  1898. },
  1899. _onSelectDate: function (e, data) {
  1900. this._handleDate(data);
  1901. this.update();
  1902. },
  1903. _onMouseEnterRange: function (e) {
  1904. var name = $(e.target).attr('name');
  1905. $('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-');
  1906. },
  1907. _onMouseOutRange: function (e) {
  1908. var name = $(e.target).attr('name');
  1909. if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider
  1910. $('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-');
  1911. },
  1912. _onMouseUpRange: function (e) {
  1913. this.d.timepickerIsActive = false;
  1914. }
  1915. };
  1916. })();
  1917. })(window, jQuery);