PageSourceSearch

https://booking.londonsteakhousecompany.com/wp-content/themes/book…/bootstrap-datetimepicker.js?ver=1790297437

js londonsteakhousecompany.com collected 2026-09-25 00:50:59 UTC 93,060 bytes, 2,707 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.