PageSourceSearch

https://farmersmarketsontario.com/_scripts/_hi5/bootstrap-datetimepicker.js

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

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.