PageSourceSearch

https://store.thebereancall.org/web/static/lib/bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js

js thebereancall.org collected 2026-09-24 19:02:24 UTC 107,357 bytes, 2,651 lines download raw bytes

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

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.