PageSourceSearch

https://dolphincambridge.com/js/1089df08fbd70d57b9767797b8414d72.js

js dolphincambridge.com collected 2026-10-10 01:19:39 UTC 103,975 bytes, 2,552 lines download raw bytes

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

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.