source: sapic/static/plugins/datepicker/bootstrap-datetimepicker.js

erwinexplicacion_situacionalgestion_usuariostaller_django
Last change on this file was 10f099e, checked in by lhernandez <lhernandez@…>, 7 años ago

Se desarrollo e implemento el registro de oraganizaciones, preparando para asociar voceros al registro

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