PageSourceSearch

https://www.rmv.de/c/typo3conf/ext/rmvtemplate/Resources/Public/JavaScript/bootstrap/bootstrap-datetime.js

js rmv.de collected 2026-10-01 10:17:09 UTC 106,585 bytes, 2,637 lines download raw bytes

1/*! version : 4.17.45
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 str
150ing
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($('<span>').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($('<span>').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($('<span>').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($('<span>').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($('<span>').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($('<span>').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($('<span>').addClass(options.icons.up))));
271                    middleRow.append($('<td>')
272                        .append($('<span>').addClass('timepicker-second').attr({ 'data-time-component': '
272seconds', '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($('<span>').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($('<span>').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($('<span>').addClass(options.icons.time))));
319                }
320                if (options.showClear) {
321                    row.push($('<td>').append($('<a>').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('<span>').addClass(options.icons.clear))));
322                }
323                if (options.showClose) {
324                    row.push($('<td>').append($('<a>').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('<span>').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 in' : '')).append(dateView));
369                }
370                if (options.toolbarPlacement === 'default') {
371                    content.append(toolbar);
372                }
373                if (hasTime()) {
374                    content.append($('<li>
374').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                currentViewMode = 0;
951                viewDate = date.clone();
952
953                return picker;
954            },
955
956            clear = function () {
957                setValue(null);
958            },
959
960            parseInputDate = function (inputDate) {
961                if (options.parseInputDate === undefined) {
962                    if (!moment.isMoment(inputDate) || inputDate instanceof Date) {
963                        inputDate = getMoment(inputDate);
964                    }
965                } else {
966                    inputDate = options.parseInputDate(inputDate);
967                }
968                //inputDate.locale(options.locale);
969                return inputDate;
970            },
971
972            /********************************************************************************
973             *
974             * Widget UI interaction functions
975             *
976             ********************************************************************************/
977            actions = {
978                next: function () {
979                    var navFnc = datePickerModes[currentViewMode].navFnc;
980                    viewDate.add(datePickerModes[currentViewMode].navStep, navFnc);
981                    fillDate();
982                    viewUpdate(navFnc);
983                },
984
985                previous: function () {
986                    var navFnc = datePickerModes[currentViewMode].navFnc;
987                    viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc);
988                    fillDate();
989                    viewUpdate(navFnc);
990                },
991
992                pickerSwitch: function () {
993                    showMode(1);
994                },
995
996                selectMonth: function (e) {
997                    var month = $(e.target).closest('tbody').find('span').index($(e.target));
998                    viewDate.month(month);
999                    if (currentViewMode === minViewModeNumber) {
1000                        setValue(date.clone().year(viewDate.year()).month(viewDate.month()));
1001                        if (!options.inline) {
1002                            hide();
1003                        }
1004                    } else {
1005                        showMode(-1);
1006                        fillDate();
1007                    }
1008                    viewUpdate('M');
1009                },
1010
1011                selectYear: function (e) {
1012                    var year = parseInt($(e.target).text(), 10) || 0;
1013                    viewDate.year(year);
1014                    if (currentViewMode === minViewModeNumber) {
1015                        setValue(date.clone().year(viewDate.year()));
1016                        if (!options.inline) {
1017                            hide();
1018                        }
1019                    } else {
1020                        showMode(-1);
1021                        fillDate();
1022                    }
1023                    viewUpdate('YYYY');
1024                },
1025
1026                selectDecade: function (e) {
1027                    var year = parseInt($(e.target).data('selection'), 10) || 0;
1028                    viewDate.year(year);
1029                    if (currentViewMode === minViewModeNumber) {
1030                        setValue(date.clone().year(viewDate.year()));
1031                        if (!options.inline) {
1032                            hide();
1033                        }
1034                    } else {
1035                        showMode(-1);
1036                        fillDate();
1037                    }
1038                    viewUpdate('YYYY');
1039                },
1040
1041                selectDay: function (e) {
1042                    var day = viewDate.clone();
1043                    if ($(e.target).is('.old')) {
1044                        day.subtract(1, 'M');
1045                    }
1046                    if ($(e.target).is('.new')) {
1047                        day.add(1, 'M');
1048                    }
1049                    setValue(day.date(parseInt($(e.target).text(), 10)));
1050                    if (!hasTime() && !options.keepOpen && !options.inline) {
1051                        hide();
1052                    }
1053                },
1054
1055                incrementHours: function () {
1056                    var newDate = date.clone().add(1, 'h');
1057                    if (isValid(newDate, 'h')) {
1058                        setValue(newDate);
1059                    }
1060                },
1061
1062                incrementMinutes: function () {
1063                    var newDate = date.clone().add(options.stepping, 'm');
1064                    if (isValid(newDate, 'm')) {
1065                        setValue(newDate);
1066                    }
1067                },
1068
1069                incrementSeconds: function () {
1070                    var newDate = date.clone().add(1, 's');
1071                    if (isValid(newDate, 's')) {
1072                        setValue(newDate);
1073                    }
1074                },
1075
1076                decrementHours: function () {
1077                    var newDate = date.clone().subtract(1, 'h');
1078                    if (isValid(newDate, 'h')) {
1079                        setValue(newDate);
1080                    }
1081                },
1082
1083                decrementMinutes: function () {
1084                    var newDate = date.clone().subtract(options.stepping, 'm');
1085                    if (isValid(newDate, 'm')) {
1086                        setValue(newDate);
1087                    }
1088                },
1089
1090                decrementSeconds: function () {
1091                    var newDate = date.clone().subtract(1, 's');
1092                    if (isValid(newDate, 's')) {
1093                        setValue(newDate);
1094                    }
1095                },
1096
1097                togglePeriod: function () {
1098                    setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'));
1099                },
1100
1101                togglePicker: function (e) {
1102                    var $this = $(e.target),
1103                        $parent = $this.closest('ul'),
1104                        expanded = $parent.find('.in'),
1105                        closed = $parent.find('.collapse:not(.in)'),
1106                        collapseData;
1107
1108                    if (expanded && expanded.length) {
1109                        collapseData = expanded.data('collapse');
1110                        if (collapseData && collapseData.transitioning) {
1111                            return;
1112                        }
1113                        if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it
1114                            expanded.collapse('hide');
1115                            closed.collapse('show');
1116                        } else { // otherwise just toggle in class on the two views
1117                            expanded.removeClass('in');
1118                            closed.addClass('in');
1119                        }
1120                        if ($this.is('span')) {
1121                            $this.toggleClass(options.icons.time + ' ' + options.icons.date);
1122                        } else {
1123                            $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
1124                        }
1125
1126                        // NOTE: uncomment if toggled state will be restored in show()
1127                        //if (component) {
1128                        //    component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
1129                        //}
1130                    }
1131                },
1132
1133                showPicker: function () {
1134                    widget.find('.timepicker > div:not(.timepicker-picker)').hide();
1135                    widget.find('.timepicker .timepicker-picker').show();
1136                },
1137
1138                showHours: function () {
1139                    widget.find('.timepicker .timepicker-picker').hide();
1140                    widget.find('.timepicker .timepicker-hours').show();
1141                },
1142
1143                showMinutes: function () {
1144                    widget.find('.timepicker .timepicker-picker').hide();
1145                    widget.find('.timepicker .timepicker-minutes').show();
1146                },
1147
1148                showSeconds: function () {
1149                    widget.find('.timepicker .timepicker-picker').hide();
1150                    widget.find('.timepicker .timepicker-seconds').show();
1151                },
1152
1153                selectHour: function (e) {
1154                    var hour = parseInt($(e.target).text(), 10);
1155
1156                    if (!use24Hours) {
1157                        if (date.hours() >= 12) {
1158                            if (hour !== 12) {
1159                                hour += 12;
1160                            }
1161                        } else {
1162                            if (hour === 12) {
1163                                hour = 0;
1164                            }
1165                        }
1166                    }
1167                    setValue(date.clone().hours(hour));
1168                    actions.showPicker.call(picker);
1169                },
1170
1171                selectMinute: function (e) {
1172                    setValue(date.clone().minutes(parseInt($(e.target).text(), 10)));
1173                    actions.showPicker.call(picker);
1174                },
1175
1176                selectSecond: function (e) {
1177                    setValue(date.clone().seconds(parseInt($(e.target).text(), 10)));
1178                    actions.showPicker.call(picker);
1179                },
1180
1181                clear: clear,
1182
1183                today: function () {
1184                    var todaysDate = getMoment();
1185                    if (isValid(todaysDate, 'd')) {
1186                        setValue(todaysDate);
1187                    }
1188                },
1189
1190                close: hide
1191            },
1192
1193            doAction = function (e) {
1194                if ($(e.currentTarget).is('.disabled')) {
1195                    return false;
1196                }
1197                actions[$(e.currentTarget).data('action')].apply(picker, arguments);
1198                return false;
1199            },
1200
1201            /**
1202             * Shows the widget. Possibly will emit dp.show and dp.change
1203             */
1204            show = function () {
1205                var currentMoment,
1206                    useCurrentGranularity = {
1207                        'year': function (m) {
1208                            return m.month(0).date(1).hours(0).seconds(0).minutes(0);
1209                        },
1210                        'month': function (m) {
1211                            return m.date(1).hours(0).seconds(0).minutes(0);
1212                        },
1213                        'day': function (m) {
1214                            return m.hours(0).seconds(0).minutes(0);
1215                        },
1216                        'hour': function (m) {
1217                            return m.seconds(0).minutes(0);
1218                        },
1219                        'minute': function (m) {
1220                            return m.seconds(0);
1221                        }
1222                    };
1223
1224                if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) {
1225                    return picker;
1226                }
1227                if (input.val() !== undefined && input.val().trim().length !== 0) {
1228                    setValue(parseInputDate(input.val().trim()));
1229                } else if (unset && options.useCurrent && (options.inline || (input.is('input') && input.val().trim().length === 0))) {
1230                    currentMoment = getMoment();
1231                    if (typeof options.useCurrent === 'string') {
1232                        currentMoment = useCurrentGranularity[options.useCurrent](currentMoment);
1233                    }
1234                    setValue(currentMoment);
1235                }
1236                widget = getTemplate();
1237
1238                fillDow();
1239                fillMonths();
1240
1241                widget.find('.timepicker-hours').hide();
1242                widget.find('.timepicker-minutes').hide();
1243                widget.find('.timepicker-seconds').hide();
1244
1245                update();
1246                showMode();
1247
1248                $(window).on('resize', place);
1249                widget.on('click', '[data-action]', doAction); // this handles clicks on the widget
1250                widget.on('mousedown', false);
1251
1252                if (component && component.hasClass('btn')) {
1253                    component.toggleClass('active');
1254                }
1255                place();
1256                widget.show();
1257                if (options.focusOnShow && !input.is(':focus')) {
1258                    input.focus();
1259                }
1260
1261                notifyEvent({
1262                    type: 'dp.show'
1263                });
1264                return picker;
1265            },
1266
1267            /**
1268             * Shows or hides the widget
1269             */
1270            toggle = function () {
1271                return (widget ? hide() : show());
1272            },
1273
1274            keydown = function (e) {
1275                var handler = null,
1276                    index,
1277                    index2,
1278                    pressedKeys = [],
1279                    pressedModifiers = {},
1280                    currentKey = e.which,
1281                    keyBindKeys,
1282                    allModifiersPressed,
1283                    pressed = 'p';
1284
1285                keyState[currentKey] = pressed;
1286
1287                for (index in keyState) {
1288                    if (keyState.hasOwnProperty(index) && keyState[index] === pressed) {
1289                        pressedKeys.push(index);
1290                        if (parseInt(index, 10) !== currentKey) {
1291                            pressedModifiers[index] = true;
1292                        }
1293                    }
1294                }
1295
1296                for (index in options.keyBinds) {
1297                    if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') {
1298                        keyBindKeys = index.split(' ');
1299                        if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) {
1300                            allModifiersPressed = true;
1301                            for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) {
1302                                if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) {
1303                                    allModifiersPressed = false;
1304                                    break;
1305                                }
1306                            }
1307                            if (allModifiersPressed) {
1308                                handler = options.keyBinds[index];
1309                                break;
1310                            }
1311                        }
1312                    }
1313                }
1314
1315                if (handler) {
1316                    handler.call(picker, widget);
1317                    e.stopPropagation();
1318                    e.preventDefault();
1319                }
1320            },
1321
1322            keyup = function (e) {
1323                keyState[e.which] = 'r';
1324                e.stopPropagation();
1325                e.preventDefault();
1326            },
1327
1328            change = function (e) {
1329                var val = $(e.target).val().trim(),
1330                    parsedDate = val ? parseInputDate(val) : null;
1331                setValue(parsedDate);
1332                e.stopImmediatePropagation();
1333                return false;
1334            },
1335
1336            attachDatePickerElementEvents = function () {
1337                input.on({
1338                    'change': change,
1339                    'blur': options.debug ? '' : hide,
1340                    'keydown': keydown,
1341                    'keyup': keyup,
1342                    'focus': options.allowInputToggle ? show : ''
1343                });
1344
1345                if (element.is('input')) {
1346                    input.on({
1347                        'focus': show
1348                    });
1349                } else if (component) {
1350                    component.on('click', toggle);
1351                    component.on('mousedown', false);
1352                }
1353            },
1354
1355            detachDatePickerElementEvents = function () {
1356                input.off({
1357                    'change': change,
1358                    'blur': blur,
1359                    'keydown': keydown,
1360                    'keyup': keyup,
1361                    'focus': options.allowInputToggle ? hide : ''
1362                });
1363
1364                if (element.is('input')) {
1365                    input.off({
1366                        'focus': show
1367                    });
1368                } else if (component) {
1369                    component.off('click', toggle);
1370                    component.off('mousedown', false);
1371                }
1372            },
1373
1374            indexGivenDates = function (givenDatesArray) {
1375                // Store given enabledDates and disabledDates as keys.
1376                // This way we can check their existence in O(1) time instead of looping through whole array.
1377                // (for example: options.enabledDates['2014-02-27'] === true)
1378                var givenDatesIndexed = {};
1379                $.each(givenDatesArray, function () {
1380                    var dDate = parseInputDate(this);
1381                    if (dDate.isValid()) {
1382                        givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true;
1383                    }
1384                });
1385                return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false;
1386            },
1387
1388            indexGivenHours = function (givenHoursArray) {
1389                // Store given enabledHours and disabledHours as keys.
1390                // This way we can check their existence in O(1) time instead of looping through whole array.
1391                // (for example: options.enabledHours['2014-02-27'] === true)
1392                var givenHoursIndexed = {};
1393                $.each(givenHoursArray, function () {
1394                    givenHoursIndexed[this] = true;
1395                });
1396                return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false;
1397            },
1398
1399            initFormatting = function () {
1400                var format = options.format || 'L LT';
1401
1402                actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) {
1403                    var newinput = date.localeData().longDateFormat(formatInput) || formatInput;
1404                    return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740
1405                        return date.localeData().longDateFormat(formatInput2) || formatInput2;
1406                    });
1407                });
1408
1409
1410                parseFormats = options.extraFormats ? options.extraFormats.slice() : [];
1411                if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) {
1412                    parseFormats.push(actualFormat);
1413                }
1414
1415                use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1);
1416
1417                if (isEnabled('y')) {
1418                    minViewModeNumber = 2;
1419                }
1420                if (isEnabled('M')) {
1421                    minViewModeNumber = 1;
1422                }
1423                if (isEnabled('d')) {
1424                    minViewModeNumber = 0;
1425                }
1426
1427                currentViewMode = Math.max(minViewModeNumber, currentViewMode);
1428
1429                if (!unset) {
1430                    setValue(date);
1431                }
1432            };
1433
1434        /********************************************************************************
1435         *
1436         * Public API functions
1437         * =====================
1438         *
1439         * Important: Do not expose direct references to private objects or the options
1440         * object to the outer world. Always return a clone when returning values or make
1441         * a clone when setting a private variable.
1442         *
1443         ********************************************************************************/
1444        picker.destroy = function () {
1445            ///<summary>Destroys the widget and removes all attached event listeners</summary>
1446            hide();
1447            detachDatePickerElementEvents();
1448            element.removeData('DateTimePicker');
1449            element.removeData('date');
1450        };
1451
1452        picker.toggle = toggle;
1453
1454        picker.show = show;
1455
1456        picker.hide = hide;
1457
1458        picker.disable = function () {
1459            ///<summary>Disables the input element, the component is attached to, by adding a disabled="true" attribute to it.
1460            ///If the widget was visible before that call it is hidden. Possibly emits dp.hide</summary>
1461            hide();
1462            if (component && component.hasClass('btn')) {
1463                component.addClass('disabled');
1464            }
1465            input.prop('disabled', true);
1466            return picker;
1467        };
1468
1469        picker.enable = function () {
1470            ///<summary>Enables the input element, the component is attached to, by removing disabled attribute from it.</summary>
1471            if (component && component.hasClass('btn')) {
1472                component.removeClass('disabled');
1473            }
1474            input.prop('disabled', false);
1475            return picker;
1476        };
1477
1478        picker.ignoreReadonly = function (ignoreReadonly) {
1479            if (arguments.length === 0) {
1480                return options.ignoreReadonly;
1481            }
1482            if (typeof ignoreReadonly !== 'boolean') {
1483                throw new TypeError('ignoreReadonly () expects a boolean parameter');
1484            }
1485            options.ignoreReadonly = ignoreReadonly;
1486            return picker;
1487        };
1488
1489        picker.options = function (newOptions) {
1490            if (arguments.length === 0) {
1491                return $.extend(true, {}, options);
1492            }
1493
1494            if (!(newOptions instanceof Object)) {
1495                throw new TypeError('options() options parameter should be an object');
1496            }
1497            $.extend(true, options, newOptions);
1498            $.each(options, function (key, value) {
1499                if (picker[key] !== undefined) {
1500                    picker[key](value);
1501                } else {
1502                    throw new TypeError('option ' + key + ' is not recognized!');
1503                }
1504            });
1505            return picker;
1506        };
1507
1508        picker.date = function (newDate) {
1509            ///<signature helpKeyword="$.fn.datetimepicker.date">
1510            ///<summary>Returns the component's model current date, a moment object or null if not set.</summary>
1511            ///<returns type="Moment">date.clone()</returns>
1512            ///</signature>
1513            ///<signature>
1514            ///<summary>Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.</summary>
1515            ///<param name="newDate" locid="$.fn.datetimepicker.date_p:newDate">Takes string, Date, moment, null parameter.</param>
1516            ///</signature>
1517            if (arguments.length === 0) {
1518                if (unset) {
1519                    return null;
1520                }
1521                return date.clone();
1522            }
1523
1524            if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
1525                throw new TypeError('date() parameter must be one of [null, string, moment or Date]');
1526            }
1527
1528            setValue(newDate === null ? null : parseInputDate(newDate));
1529            return picker;
1530        };
1531
1532        picker.format = function (newFormat) {
1533            ///<summary>test su</summary>
1534            ///<param name="newFormat">info about para</param>
1535            ///<returns type="string|boolean">returns foo</returns>
1536            if (arguments.length === 0) {
1537                return options.format;
1538            }
1539
1540            if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) {
1541                throw new TypeError('format() expects a string or boolean:false parameter ' + newFormat);
1542            }
1543
1544            options.format = newFormat;
1545            if (actualFormat) {
1546                initFormatting(); // reinit formatting
1547            }
1548            return picker;
1549        };
1550
1551        picker.timeZone = function (newZone) {
1552            if (arguments.length === 0) {
1553                return options.timeZone;
1554            }
1555
1556            if (typeof newZone !== 'string') {
1557                throw new TypeError('newZone() expects a string parameter');
1558            }
1559
1560            options.timeZone = newZone;
1561
1562            return picker;
1563        };
1564
1565        picker.dayViewHeaderFormat = function (newFormat) {
1566            if (arguments.length === 0) {
1567                return options.dayViewHeaderFormat;
1568            }
1569
1570            if (typeof newFormat !== 'string') {
1571                throw new TypeError('dayViewHeaderFormat() expects a string parameter');
1572            }
1573
1574            options.dayViewHeaderFormat = newFormat;
1575            return picker;
1576        };
1577
1578        picker.extraFormats = function (formats) {
1579            if (arguments.length === 0) {
1580                return options.extraFormats;
1581            }
1582
1583            if (formats !== false && !(formats instanceof Array)) {
1584                throw new TypeError('extraFormats() expects an array or false parameter');
1585            }
1586
1587            options.extraFormats = formats;
1588            if (parseFormats) {
1589                initFormatting(); // reinit formatting
1590            }
1591            return picker;
1592        };
1593
1594        picker.disabledDates = function (dates) {
1595            ///<signature helpKeyword="$.fn.datetimepicker.disabledDates">
1596            ///<summary>Returns an array with the currently set disabled dates on the component.</summary>
1597            ///<returns type="array">options.disabledDates</returns>
1598            ///</signature>
1599            ///<signature>
1600            ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
1601            ///options.enabledDates if such exist.</summary>
1602            ///<param name="dates" locid="$.fn.datetimepicker.disabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.
1602</param>
1603            ///</signature>
1604            if (arguments.length === 0) {
1605                return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates);
1606            }
1607
1608            if (!dates) {
1609                options.disabledDates = false;
1610                update();
1611                return picker;
1612            }
1613            if (!(dates instanceof Array)) {
1614                throw new TypeError('disabledDates() expects an array parameter');
1615            }
1616            options.disabledDates = indexGivenDates(dates);
1617            options.enabledDates = false;
1618            update();
1619            return picker;
1620        };
1621
1622        picker.enabledDates = function (dates) {
1623            ///<signature helpKeyword="$.fn.datetimepicker.enabledDates">
1624            ///<summary>Returns an array with the currently set enabled dates on the component.</summary>
1625            ///<returns type="array">options.enabledDates</returns>
1626            ///</signature>
1627            ///<signature>
1628            ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist.</summary>
1629            ///<param name="dates" locid="$.fn.datetimepicker.enabledDates_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
1630            ///</signature>
1631            if (arguments.length === 0) {
1632                return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates);
1633            }
1634
1635            if (!dates) {
1636                options.enabledDates = false;
1637                update();
1638                return picker;
1639            }
1640            if (!(dates instanceof Array)) {
1641                throw new TypeError('enabledDates() expects an array parameter');
1642            }
1643            options.enabledDates = indexGivenDates(dates);
1644            options.disabledDates = false;
1645            update();
1646            return picker;
1647        };
1648
1649        picker.daysOfWeekDisabled = function (daysOfWeekDisabled) {
1650            if (arguments.length === 0) {
1651                return options.daysOfWeekDisabled.splice(0);
1652            }
1653
1654            if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) {
1655                options.daysOfWeekDisabled = false;
1656                update();
1657                return picker;
1658            }
1659
1660            if (!(daysOfWeekDisabled instanceof Array)) {
1661                throw new TypeError('daysOfWeekDisabled() expects an array parameter');
1662            }
1663            options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) {
1664                currentValue = parseInt(currentValue, 10);
1665                if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) {
1666                    return previousValue;
1667                }
1668                if (previousValue.indexOf(currentValue) === -1) {
1669                    previousValue.push(currentValue);
1670                }
1671                return previousValue;
1672            }, []).sort();
1673            if (options.useCurrent && !options.keepInvalid) {
1674                var tries = 0;
1675                while (!isValid(date, 'd')) {
1676                    date.add(1, 'd');
1677                    if (tries === 31) {
1678                        throw 'Tried 31 times to find a valid date';
1679                    }
1680                    tries++;
1681                }
1682                setValue(date);
1683            }
1684            update();
1685            return picker;
1686        };
1687
1688        picker.maxDate = function (maxDate) {
1689            if (arguments.length === 0) {
1690                return options.maxDate ? options.maxDate.clone() : options.maxDate;
1691            }
1692
1693            if ((typeof maxDate === 'boolean') && maxDate === false) {
1694                options.maxDate = false;
1695                update();
1696                return picker;
1697            }
1698
1699            if (typeof maxDate === 'string') {
1700                if (maxDate === 'now' || maxDate === 'moment') {
1701                    maxDate = getMoment();
1702                }
1703            }
1704
1705            var parsedDate = parseInputDate(maxDate);
1706
1707            if (!parsedDate.isValid()) {
1708                throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate);
1709            }
1710            if (options.minDate && parsedDate.isBefore(options.minDate)) {
1711                throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat));
1712            }
1713            options.maxDate = parsedDate;
1714            if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) {
1715                setValue(options.maxDate);
1716            }
1717            if (viewDate.isAfter(parsedDate)) {
1718                viewDate = parsedDate.clone().subtract(options.stepping, 'm');
1719            }
1720            update();
1721            return picker;
1722        };
1723
1724        picker.minDate = function (minDate) {
1725            if (arguments.length === 0) {
1726                return options.minDate ? options.minDate.clone() : options.minDate;
1727            }
1728
1729            if ((typeof minDate === 'boolean') && minDate === false) {
1730                options.minDate = false;
1731                update();
1732                return picker;
1733            }
1734
1735            if (typeof minDate === 'string') {
1736                if (minDate === 'now' || minDate === 'moment') {
1737                    minDate = getMoment();
1738                }
1739            }
1740
1741            var parsedDate = parseInputDate(minDate);
1742
1743            if (!parsedDate.isValid()) {
1744                throw new TypeError('minDate() Could not parse date parameter: ' + minDate);
1745            }
1746            if (options.maxDate && parsedDate.isAfter(options.maxDate)) {
1747                throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat));
1748            }
1749            options.minDate = parsedDate;
1750            if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) {
1751                setValue(options.minDate);
1752            }
1753            if (viewDate.isBefore(parsedDate)) {
1754                viewDate = parsedDate.clone().add(options.stepping, 'm');
1755            }
1756            update();
1757            return picker;
1758        };
1759
1760        picker.defaultDate = function (defaultDate) {
1761            ///<signature helpKeyword="$.fn.datetimepicker.defaultDate">
1762            ///<summary>Returns a moment with the options.defaultDate option configuration or false if not set</summary>
1763            ///<returns type="Moment">date.clone()</returns>
1764            ///</signature>
1765            ///<signature>
1766            ///<summary>Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared.</summary>
1767            ///<param name="defaultDate" locid="$.fn.datetimepicker.defaultDate_p:defaultDate">Takes a string, Date, moment, boolean:false</param>
1768            ///</signature>
1769            if (arguments.length === 0) {
1770                return options.defaultDate ? options.defaultDate.clone() : options.defaultDate;
1771            }
1772            if (!defaultDate) {
1773                options.defaultDate = false;
1774                return picker;
1775            }
1776
1777            if (typeof defaultDate === 'string') {
1778                if (defaultDate === 'now' || defaultDate === 'moment') {
1779                    defaultDate = getMoment();
1780                } else {
1781                    defaultDate = getMoment(defaultDate);
1782                }
1783            }
1784
1785            var parsedDate = parseInputDate(defaultDate);
1786            if (!parsedDate.isValid()) {
1787                throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate);
1788            }
1789            if (!isValid(parsedDate)) {
1790                throw new TypeError('defaultDate() date passed is invalid according to component setup validations');
1791            }
1792
1793            options.defaultDate = parsedDate;
1794
1795            if ((options.defaultDate && options.inline) || input.val().trim() === '') {
1796                setValue(options.defaultDate);
1797            }
1798            return picker;
1799        };
1800
1801        picker.locale = function (locale) {
1802            if (arguments.length === 0) {
1803                return options.locale;
1804            }
1805
1806            if (!moment.localeData(locale)) {
1807                throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!');
1808            }
1809
1810            options.locale = locale;
1811            date.locale(options.locale);
1812            viewDate.locale(options.locale);
1813
1814            if (actualFormat) {
1815                initFormatting(); // reinit formatting
1816            }
1817            if (widget) {
1818                hide();
1819                show();
1820            }
1821            return picker;
1822        };
1823
1824        picker.stepping = function (stepping) {
1825            if (arguments.length === 0) {
1826                return options.stepping;
1827            }
1828
1829            stepping = parseInt(stepping, 10);
1830            if (isNaN(stepping) || stepping < 1) {
1831                stepping = 1;
1832            }
1833            options.stepping = stepping;
1834            return picker;
1835        };
1836
1837        picker.useCurrent = function (useCurrent) {
1838            var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute'];
1839            if (arguments.length === 0) {
1840                return options.useCurrent;
1841            }
1842
1843            if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) {
1844                throw new TypeError('useCurrent() expects a boolean or string parameter');
1845            }
1846            if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) {
1847                throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', '));
1848            }
1849            options.useCurrent = useCurrent;
1850            return picker;
1851        };
1852
1853        picker.collapse = function (collapse) {
1854            if (arguments.length === 0) {
1855                return options.collapse;
1856            }
1857
1858            if (typeof collapse !== 'boolean') {
1859                throw new TypeError('collapse() expects a boolean parameter');
1860            }
1861            if (options.collapse === collapse) {
1862                return picker;
1863            }
1864            options.collapse = collapse;
1865            if (widget) {
1866                hide();
1867                show();
1868            }
1869            return picker;
1870        };
1871
1872        picker.icons = function (icons) {
1873            if (arguments.length === 0) {
1874                return $.extend({}, options.icons);
1875            }
1876
1877            if (!(icons instanceof Object)) {
1878                throw new TypeError('icons() expects parameter to be an Object');
1879            }
1880            $.extend(options.icons, icons);
1881            if (widget) {
1882                hide();
1883                show();
1884            }
1885            return picker;
1886        };
1887
1888        picker.tooltips = function (tooltips) {
1889            if (arguments.length === 0) {
1890                return $.extend({}, options.tooltips);
1891            }
1892
1893            if (!(tooltips instanceof Object)) {
1894                throw new TypeError('tooltips() expects parameter to be an Object');
1895            }
1896            $.extend(options.tooltips, tooltips);
1897            if (widget) {
1898                hide();
1899                show();
1900            }
1901            return picker;
1902        };
1903
1904        picker.useStrict = function (useStrict) {
1905            if (arguments.length === 0) {
1906                return options.useStrict;
1907            }
1908
1909            if (typeof useStrict !== 'boolean') {
1910                throw new TypeError('useStrict() expects a boolean parameter');
1911            }
1912            options.useStrict = useStrict;
1913            return picker;
1914        };
1915
1916        picker.sideBySide = function (sideBySide) {
1917            if (arguments.length === 0) {
1918                return options.sideBySide;
1919            }
1920
1921            if (typeof sideBySide !== 'boolean') {
1922                throw new TypeError('sideBySide() expects a boolean parameter');
1923            }
1924            options.sideBySide = sideBySide;
1925            if (widget) {
1926                hide();
1927                show();
1928            }
1929            return picker;
1930        };
1931
1932        picker.viewMode = function (viewMode) {
1933            if (arguments.length === 0) {
1934                return options.viewMode;
1935            }
1936
1937            if (typeof viewMode !== 'string') {
1938                throw new TypeError('viewMode() expects a string parameter');
1939            }
1940
1941            if (viewModes.indexOf(viewMode) === -1) {
1942                throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value');
1943            }
1944
1945            options.viewMode = viewMode;
1946            currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber);
1947
1948            showMode();
1949            return picker;
1950        };
1951
1952        picker.toolbarPlacement = function (toolbarPlacement) {
1953            if (arguments.length === 0) {
1954                return options.toolbarPlacement;
1955            }
1956
1957            if (typeof toolbarPlacement !== 'string') {
1958                throw new TypeError('toolbarPlacement() expects a string parameter');
1959            }
1960            if (toolbarPlacements.indexOf(toolbarPlacement) === -1) {
1961                throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value');
1962            }
1963            options.toolbarPlacement = toolbarPlacement;
1964
1965            if (widget) {
1966                hide();
1967                show();
1968            }
1969            return picker;
1970        };
1971
1972        picker.widgetPositioning = function (widgetPositioning) {
1973            if (arguments.length === 0) {
1974                return $.extend({}, options.widgetPositioning);
1975            }
1976
1977            if (({}).toString.call(widgetPositioning) !== '[object Object]') {
1978                throw new TypeError('widgetPositioning() expects an object variable');
1979            }
1980            if (widgetPositioning.horizontal) {
1981                if (typeof widgetPositioning.horizontal !== 'string') {
1982                    throw new TypeError('widgetPositioning() horizontal variable must be a string');
1983                }
1984                widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase();
1985                if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) {
1986                    throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')');
1987                }
1988                options.widgetPositioning.horizontal = widgetPositioning.horizontal;
1989            }
1990            if (widgetPositioning.vertical) {
1991                if (typeof widgetPositioning.vertical !== 'string') {
1992                    throw new TypeError('widgetPositioning() vertical variable must be a string');
1993                }
1994                widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase();
1995                if (verticalModes.indexOf(widgetPositioning.vertical) === -1) {
1996                    throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')');
1997                }
1998                options.widgetPositioning.vertical = widgetPositioning.vertical;
1999            }
2000            update();
2001            return picker;
2002        };
2003
2004        picker.calendarWeeks = function (calendarWeeks) {
2005            if (arguments.length === 0) {
2006                return options.calendarWeeks;
2007            }
2008
2009            if (typeof calendarWeeks !== 'boolean') {
2010                throw new TypeError('calendarWeeks() expects parameter to be a boolean value');
2011            }
2012
2013            options.calendarWeeks = calendarWeeks;
2014            update();
2015            return picker;
2016        };
2017
2018        picker.showTodayButton = function (showTodayButton) {
2019            if (arguments.length === 0) {
2020                return options.showTodayButton;
2021            }
2022
2023            if (typeof showTodayButton !== 'boolean') {
2024                throw new TypeError('showTodayButton() expects a boolean parameter');
2025            }
2026
2027            options.showTodayButton = showTodayButton;
2028            if (widget) {
2029                hide();
2030                show();
2031            }
2032            return picker;
2033        };
2034
2035        picker.showClear = function (showClear) {
2036            if (arguments.length === 0) {
2037                return options.showClear;
2038            }
2039
2040            if (typeof showClear !== 'boolean') {
2041                throw new TypeError('showClear() expects a boolean parameter');
2042            }
2043
2044            options.showClear = showClear;
2045            if (widget) {
2046                hide();
2047                show();
2048            }
2049            return picker;
2050        };
2051
2052        picker.widgetParent = function (widgetParent) {
2053            if (arguments.length === 0) {
2054                return options.widgetParent;
2055            }
2056
2057            if (typeof widgetParent === 'string') {
2058                widgetParent = $(widgetParent);
2059            }
2060
2061            if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) {
2062                throw new TypeError('widgetParent() expects a string or a jQuery object parameter');
2063            }
2064
2065            options.widgetParent = widgetParent;
2066            if (widget) {
2067                hide();
2068                show();
2069            }
2070            return picker;
2071        };
2072
2073        picker.keepOpen = function (keepOpen) {
2074            if (arguments.length === 0) {
2075                return options.keepOpen;
2076            }
2077
2078            if (typeof keepOpen !== 'boolean') {
2079                throw new TypeError('keepOpen() expects a boolean parameter');
2080            }
2081
2082            options.keepOpen = keepOpen;
2083            return picker;
2084        };
2085
2086        picker.focusOnShow = function (focusOnShow) {
2087            if (arguments.length === 0) {
2088                return options.focusOnShow;
2089            }
2090
2091            if (typeof focusOnShow !== 'boolean') {
2092                throw new TypeError('focusOnShow() expects a boolean parameter');
2093            }
2094
2095            options.focusOnShow = focusOnShow;
2096            return picker;
2097        };
2098
2099        picker.inline = function (inline) {
2100            if (arguments.length === 0) {
2101                return options.inline;
2102            }
2103
2104            if (typeof inline !== 'boolean') {
2105                throw new TypeError('inline() expects a boolean parameter');
2106            }
2107
2108            options.inline = inline;
2109            return picker;
2110        };
2111
2112        picker.clear = function () {
2113            clear();
2114            return picker;
2115        };
2116
2117        picker.keyBinds = function (keyBinds) {
2118            if (arguments.length === 0) {
2119                return options.keyBinds;
2120            }
2121
2122            options.keyBinds = keyBinds;
2123            return picker;
2124        };
2125
2126        picker.getMoment = function (d) {
2127            return getMoment(d);
2128        };
2129
2130        picker.debug = function (debug) {
2131            if (typeof debug !== 'boolean') {
2132                throw new TypeError('debug() expects a boolean parameter');
2133            }
2134
2135            options.debug = debug;
2136            return picker;
2137        };
2138
2139        picker.allowInputToggle = function (allowInputToggle) {
2140            if (arguments.length === 0) {
2141                return options.allowInputToggle;
2142            }
2143
2144            if (typeof allowInputToggle !== 'boolean') {
2145                throw new TypeError('allowInputToggle() expects a boolean parameter');
2146            }
2147
2148            options.allowInputToggle = allowInputToggle;
2149            return picker;
2150        };
2151
2152        picker.showClose = function (showClose) {
2153            if (arguments.length === 0) {
2154                return options.showClose;
2155            }
2156
2157            if (typeof showClose !== 'boolean') {
2158                throw new TypeError('showClose() expects a boolean parameter');
2159            }
2160
2161            options.showClose = showClose;
2162            return picker;
2163        };
2164
2165        picker.keepInvalid = function (keepInvalid) {
2166            if (arguments.length === 0) {
2167                return options.keepInvalid;
2168            }
2169
2170            if (typeof keepInvalid !== 'boolean') {
2171                throw new TypeError('keepInvalid() expects a boolean parameter');
2172            }
2173            options.keepInvalid = keepInvalid;
2174            return picker;
2175        };
2176
2177        picker.datepickerInput = function (datepickerInput) {
2178            if (arguments.length === 0) {
2179                return options.datepickerInput;
2180            }
2181
2182            if (typeof datepickerInput !== 'string') {
2183                throw new TypeError('datepickerInput() expects a string parameter');
2184            }
2185
2186            options.datepickerInput = datepickerInput;
2187            return picker;
2188        };
2189
2190        picker.parseInputDate = function (parseInputDate) {
2191            if (arguments.length === 0) {
2192                return options.parseInputDate;
2193            }
2194
2195            if (typeof parseInputDate !== 'function') {
2196                throw new TypeError('parseInputDate() sholud be as function');
2197            }
2198
2199            options.parseInputDate = parseInputDate;
2200
2201            return picker;
2202        };
2203
2204        picker.disabledTimeIntervals = function (disabledTimeIntervals) {
2205            ///<signature helpKeyword="$.fn.datetimepicker.disabledTimeIntervals">
2206            ///<summary>Returns an array with the currently set disabled dates on the component.</summary>
2207            ///<returns type="array">options.disabledTimeIntervals</returns>
2208            ///</signature>
2209            ///<signature>
2210            ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
2211            ///options.enabledDates if such exist.</summary>
2212            ///<param name="dates" locid="$.fn.datetimepicker.disabledTimeIntervals_p:dates">Takes an [ string or Date or moment ] of values and allows the user to select only from those days.</param>
2213            ///</signature>
2214            if (arguments.length === 0) {
2215                return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals);
2216            }
2217
2218            if (!disabledTimeIntervals) {
2219                options.disabledTimeIntervals = false;
2220                update();
2221                return picker;
2222            }
2223            if (!(disabledTimeIntervals instanceof Array)) {
2224                throw new TypeError('disabledTimeIntervals() expects an array parameter');
2225            }
2226            options.disabledTimeIntervals = disabledTimeIntervals;
2227            update();
2228            return picker;
2229        };
2230
2231        picker.disabledHours = function (hours) {
2232            ///<signature helpKeyword="$.fn.datetimepicker.disabledHours">
2233            ///<summary>Returns an array with the currently set disabled hours on the component.</summary>
2234            ///<returns type="array">options.disabledHours</returns>
2235            ///</signature>
2236            ///<signature>
2237            ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of
2238            ///options.enabledHours if such exist.</summary>
2239            ///<param name="hours" locid="$.fn.datetimepicker.disabledHours_p:hours">Takes an [ int ] of values and disallows the user to select only from those hours.</param>
2240            ///</signature>
2241            if (arguments.length === 0) {
2242                return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours);
2243            }
2244
2245            if (!hours) {
2246                options.disabledHours = false;
2247                update();
2248                return picker;
2249            }
2250            if (!(hours instanceof Array)) {
2251                throw new TypeError('disabledHours() expects an array parameter');
2252            }
2253            options.disabledHours = indexGivenHours(hours);
2254            options.enabledHours = false;
2255            if (options.useCurrent && !options.keepInvalid) {
2256                var tries = 0;
2257                while (!isValid(date, 'h')) {
2258                    date.add(1, 'h');
2259                    if (tries === 24) {
2260                        throw 'Tried 24 times to find a valid date';
2261                    }
2262                    tries++;
2263                }
2264                setValue(date);
2265            }
2266            update();
2267            return picker;
2268        };
2269
2270        picker.enabledHours = function (hours) {
2271            ///<signature helpKeyword="$.fn.datetimepicker.enabledHours">
2272            ///<summary>Returns an array with the currently set enabled hours on the component.</summary>
2273            ///<returns type="array">options.enabledHours</returns>
2274            ///</signature>
2275            ///<signature>
2276            ///<summary>Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist.</summary>
2277            ///<param name="hours" locid="$.fn.datetimepicker.enabledHours_p:hours">Takes an [ int ] of values and allows the user to select only from those hours.</param>
2278            ///</signature>
2279            if (arguments.length === 0) {
2280                return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours);
2281            }
2282
2283            if (!hours) {
2284                options.enabledHours = false;
2285                update();
2286                return picker;
2287            }
2288            if (!(hours instanceof Array)) {
2289                throw new TypeError('enabledHours() expects an array parameter');
2290            }
2291            options.enabledHours = indexGivenHours(hours);
2292            options.disabledHours = false;
2293            if (options.useCurrent && !options.keepInvalid) {
2294                var tries = 0;
2295                while (!isValid(date, 'h')) {
2296                    date.add(1, 'h');
2297                    if (tries === 24) {
2298                        throw 'Tried 24 times to find a valid date';
2299                    }
2300                    tries++;
2301                }
2302                setValue(date);
2303            }
2304            update();
2305            return picker;
2306        };
2307        /**
2308         * Returns the component's model current viewDate, a moment object or null if not set. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration.
2309         * @param {Takes string, viewDate, moment, null parameter.} newDate
2310         * @returns {viewDate.clone()}
2311         */
2312        picker.viewDate = function (newDate) {
2313            if (arguments.length === 0) {
2314                return viewDate.clone();
2315            }
2316
2317            if (!newDate) {
2318                viewDate = date.clone();
2319                return picker;
2320            }
2321
2322            if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
2323                throw new TypeError('viewDate() parameter must be one of [string, moment or Date]');
2324            }
2325
2326            viewDate = parseInputDate(newDate);
2327            viewUpdate();
2328            return picker;
2329        };
2330
2331        // initializing element and component attributes
2332        if (element.is('input')) {
2333            input = element;
2334        } else {
2335            input = element.find(options.datepickerInput);
2336            if (input.length === 0) {
2337                input = element.find('input');
2338            } else if (!input.is('input')) {
2339                throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element');
2340            }
2341        }
2342
2343        if (element.hasClass('input-group')) {
2344            // in case there is more then one 'input-group-addon' Issue #48
2345            if (element.find('.btn-datepicker-popup').length === 0) {
2346                component = element.find('.input-group-addon');
2347            } else {
2348                component = element.find('.btn-datepicker-popup');
2349            }
2350        }
2351
2352        if (!options.inline && !input.is('input')) {
2353            throw new Error('Could not initialize DateTimePicker without an input element');
2354        }
2355
2356        // Set defaults for date here now instead of in var declaration
2357        date = getMoment();
2358        viewDate = date.clone();
2359
2360        $.extend(true, options, dataToOptions());
2361
2362        picker.options(options);
2363
2364        initFormatting();
2365
2366        attachDatePickerElementEvents();
2367
2368        if (input.prop('disabled')) {
2369            picker.disable();
2370        }
2371        if (input.is('input') && input.val().trim().length !== 0) {
2372            setValue(parseInputDate(input.val().trim()));
2373        }
2374        else if (options.defaultDate && input.attr('placeholder') 
2374=== undefined) {
2375            setValue(options.defaultDate);
2376        }
2377        if (options.inline) {
2378            show();
2379        }
2380        return picker;
2381    };
2382
2383    /********************************************************************************
2384     *
2385     * jQuery plugin constructor and defaults object
2386     *
2387     ********************************************************************************/
2388
2389    /**
2390     * See (http://jquery.com/).
2391     * @name jQuery
2392     * @class
2393     * See the jQuery Library  (http://jquery.com/) for full details.  This just
2394     * documents the function and classes that are added to jQuery by this plug-in.
2395     */
2396    /**
2397     * See (http://jquery.com/)
2398     * @name fn
2399     * @class
2400     * See the jQuery Library  (http://jquery.com/) for full details.  This just
2401     * documents the function and classes that are added to jQuery by this plug-in.
2402     * @memberOf jQuery
2403     */
2404    /**
2405     * Show comments
2406     * @class datetimepicker
2407     * @memberOf jQuery.fn
2408     */
2409    $.fn.datetimepicker = function (options) {
2410        options = options || {};
2411
2412        var args = Array.prototype.slice.call(arguments, 1),
2413            isInstance = true,
2414            thisMethods = ['destroy', 'hide', 'show', 'toggle'],
2415            returnValue;
2416
2417        if (typeof options === 'object') {
2418            return this.each(function () {
2419                var $this = $(this),
2420                    _options;
2421                if (!$this.data('DateTimePicker')) {
2422                    // create a private copy of the defaults object
2423                    _options = $.extend(true, {}, $.fn.datetimepicker.defaults, options);
2424                    $this.data('DateTimePicker', dateTimePicker($this, _options));
2425                }
2426            });
2427        } else if (typeof options === 'string') {
2428            this.each(function () {
2429                var $this = $(this),
2430                    instance = $this.data('DateTimePicker');
2431                if (!instance) {
2432                    throw new Error('bootstrap-datetimepicker("' + options + '") method was called on an element that is not using DateTimePicker');
2433                }
2434
2435                returnValue = instance[options].apply(instance, args);
2436                isInstance = returnValue === instance;
2437            });
2438
2439            if (isInstance || $.inArray(options, thisMethods) > -1) {
2440                return this;
2441            }
2442
2443            return returnValue;
2444        }
2445
2446        throw new TypeError('Invalid arguments for DateTimePicker: ' + options);
2447    };
2448
2449    $.fn.datetimepicker.defaults = {
2450        timeZone: '',
2451        format: false,
2452        dayViewHeaderFormat: 'MMMM YYYY',
2453        extraFormats: false,
2454        stepping: 1,
2455        minDate: false,
2456        maxDate: false,
2457        useCurrent: true,
2458        collapse: true,
2459        locale: moment.locale(),
2460        defaultDate: false,
2461        disabledDates: false,
2462        enabledDates: false,
2463        icons: {
2464            time: 'glyphicon glyphicon-time',
2465            date: 'glyphicon glyphicon-calendar',
2466            up: 'glyphicon glyphicon-chevron-up',
2467            down: 'glyphicon glyphicon-chevron-down',
2468            previous: 'glyphicon glyphicon-chevron-left',
2469            next: 'glyphicon glyphicon-chevron-right',
2470            today: 'glyphicon glyphicon-screenshot',
2471            clear: 'glyphicon glyphicon-trash',
2472            close: 'glyphicon glyphicon-remove'
2473        },
2474        tooltips: {
2475            today: 'Go to today',
2476            clear: 'Clear selection',
2477            close: 'Close the picker',
2478            selectMonth: 'Select Month',
2479            prevMonth: 'Previous Month',
2480            nextMonth: 'Next Month',
2481            selectYear: 'Select Year',
2482            prevYear: 'Previous Year',
2483            nextYear: 'Next Year',
2484            selectDecade: 'Select Decade',
2485            prevDecade: 'Previous Decade',
2486            nextDecade: 'Next Decade',
2487            prevCentury: 'Previous Century',
2488            nextCentury: 'Next Century',
2489            pickHour: 'Pick Hour',
2490            incrementHour: 'Increment Hour',
2491            decrementHour: 'Decrement Hour',
2492            pickMinute: 'Pick Minute',
2493            incrementMinute: 'Increment Minute',
2494            decrementMinute: 'Decrement Minute',
2495            pickSecond: 'Pick Second',
2496            incrementSecond: 'Increment Second',
2497            decrementSecond: 'Decrement Second',
2498            togglePeriod: 'Toggle Period',
2499            selectTime: 'Select Time'
2500        },
2501        useStrict: false,
2502        sideBySide: false,
2503        daysOfWeekDisabled: false,
2504        calendarWeeks: false,
2505        viewMode: 'days',
2506        toolbarPlacement: 'default',
2507        showTodayButton: false,
2508        showClear: false,
2509        showClose: false,
2510        widgetPositioning: {
2511            horizontal: 'auto',
2512            vertical: 'auto'
2513        },
2514        widgetParent: null,
2515        ignoreReadonly: false,
2516        keepOpen: false,
2517        focusOnShow: true,
2518        inline: false,
2519        keepInvalid: false,
2520        datepickerInput: '.datepickerinput',
2521        keyBinds: {
2522            up: function (widget) {
2523                if (!widget) {
2524                    return;
2525                }
2526                var d = this.date() || this.getMoment();
2527                if (widget.find('.datepicker').is(':visible')) {
2528                    this.date(d.clone().subtract(7, 'd'));
2529                } else {
2530                    this.date(d.clone().add(this.stepping(), 'm'));
2531                }
2532            },
2533            down: function (widget) {
2534                if (!widget) {
2535                    this.show();
2536                    return;
2537                }
2538                var d = this.date() || this.getMoment();
2539                if (widget.find('.datepicker').is(':visible')) {
2540                    this.date(d.clone().add(7, 'd'));
2541                } else {
2542                    this.date(d.clone().subtract(this.stepping(), 'm'));
2543                }
2544            },
2545            'control up': function (widget) {
2546                if (!widget) {
2547                    return;
2548                }
2549                var d = this.date() || this.getMoment();
2550                if (widget.find('.datepicker').is(':visible')) {
2551                    this.date(d.clone().subtract(1, 'y'));
2552                } else {
2553                    this.date(d.clone().add(1, 'h'));
2554                }
2555            },
2556            'control down': function (widget) {
2557                if (!widget) {
2558                    return;
2559                }
2560                var d = this.date() || this.getMoment();
2561                if (widget.find('.datepicker').is(':visible')) {
2562                    this.date(d.clone().add(1, 'y'));
2563                } else {
2564                    this.date(d.clone().subtract(1, 'h'));
2565                }
2566            },
2567            left: function (widget) {
2568                if (!widget) {
2569                    return;
2570                }
2571                var d = this.date() || this.getMoment();
2572                if (widget.find('.datepicker').is(':visible')) {
2573                    this.date(d.clone().subtract(1, 'd'));
2574                }
2575            },
2576            right: function (widget) {
2577                if (!widget) {
2578                    return;
2579                }
2580                var d = this.date() || this.getMoment();
2581                if (widget.find('.datepicker').is(':visible')) {
2582                    this.date(d.clone().add(1, 'd'));
2583                }
2584            },
2585            pageUp: function (widget) {
2586                if (!widget) {
2587                    return;
2588                }
2589                var d = this.date() || this.getMoment();
2590                if (widget.find('.datepicker').is(':visible')) {
2591                    this.date(d.clone().subtract(1, 'M'));
2592                }
2593            },
2594            pageDown: function (widget) {
2595                if (!widget) {
2596                    return;
2597                }
2598                var d = this.date() || this.getMoment();
2599                if (widget.find('.datepicker').is(':visible')) {
2600                    this.date(d.clone().add(1, 'M'));
2601                }
2602            },
2603            enter: function () {
2604                this.hide();
2605            },
2606            escape: function () {
2607                this.hide();
2608            },
2609            //tab: function (widget) { //this break the flow of the form. disabling for now
2610            //    var toggle = widget.find('.picker-switch a[data-action="togglePicker"]');
2611            //    if(toggle.length > 0) toggle.click();
2612            //},
2613            'control space': function (widget) {
2614                if (!widget) {
2615                    return;
2616                }
2617                if (widget.find('.timepicker').is(':visible')) {
2618                    widget.find('.btn[data-action="togglePeriod"]').click();
2619                }
2620            },
2621            t: function () {
2622                this.date(this.getMoment());
2623            },
2624            'delete': function () {
2625                this.clear();
2626            }
2627        },
2628        debug: false,
2629        allowInputToggle: false,
2630        disabledTimeIntervals: false,
2631        disabledHours: false,
2632        enabledHours: false,
2633        viewDate: false
2634    };
2635
2636    return $.fn.datetimepicker;
2637}));

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