PageSourceSearch

https://hanokers.org/js/bootstrap-datetimepicker.min.js

js hanokers.org collected 2026-09-30 11:26:57 UTC 104,045 bytes, 2,507 lines download raw bytes

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

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.