PageSourceSearch

https://paradizoa.com/static/js/bootstrap-datetimepicker.js

js paradizoa.com collected 2026-10-08 21:17:03 UTC 107,334 bytes, 2,652 lines download raw bytes

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

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.