Administrator
2022-09-14 58d006e05dcf2a20d0ec5367dd03d66a61db6849
提交 | 用户 | 时间
58d006 1 /* =========================================================
A 2  * bootstrap-datepicker.js
3  * http://www.eyecon.ro/bootstrap-datepicker
4  * =========================================================
5  * Copyright 2012 Stefan Petre
6  * Improvements by Andrew Rowls
7  * Improvements By Keenthemes for Boostrap 3.0 support
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ========================================================= */
21
22 (function( $ ) {
23
24     var $window = $(window);
25
26     function UTCDate(){
27         return new Date(Date.UTC.apply(Date, arguments));
28     }
29     function UTCToday(){
30         var today = new Date();
31         return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
32     }
33
34
35     // Picker object
36
37     var Datepicker = function(element, options) {
38         var that = this;
39
40         this._process_options(options);
41
42         this.element = $(element);
43         this.isInline = false;
44         this.isInput = this.element.is('input');
45         this.component = this.element.is('.date') ? this.element.find('.input-group-btn, .btn') : false;
46         this.hasInput = this.component && this.element.find('input').length;
47         if(this.component && this.component.length === 0)
48             this.component = false;
49
50         this.picker = $(DPGlobal.template);
51         this._buildEvents();
52         this._attachEvents();
53
54         if(this.isInline) {
55             this.picker.addClass('datepicker-inline').appendTo(this.element);
56         } else {
57             this.picker.addClass('datepicker-dropdown dropdown-menu');
58         }
59
60         if (this.o.rtl){
61             this.picker.addClass('datepicker-rtl');
62             this.picker.find('.prev i, .next i')
63                         .toggleClass('fa-angle-left fa-angle-right');
64         }
65
66
67         this.viewMode = this.o.startView;
68
69         if (this.o.calendarWeeks)
70             this.picker.find('tfoot th.today')
71                         .attr('colspan', function(i, val){
72                             return parseInt(val) + 1;
73                         });
74
75         this._allow_update = false;
76
77         this.setStartDate(this._o.startDate);
78         this.setEndDate(this._o.endDate);
79         this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
80
81         this.fillDow();
82         this.fillMonths();
83
84         this._allow_update = true;
85
86         this.update();
87         this.showMode();
88
89         if(this.isInline) {
90             this.show();
91         }
92     };
93
94     Datepicker.prototype = {
95         constructor: Datepicker,
96
97         _process_options: function(opts){
98             // Store raw options for reference
99             this._o = $.extend({}, this._o, opts);
100             // Processed options
101             var o = this.o = $.extend({}, this._o);
102
103             // Check if "de-DE" style date is available, if not language should
104             // fallback to 2 letter code eg "de"
105             var lang = o.language;
106             if (!dates[lang]) {
107                 lang = lang.split('-')[0];
108                 if (!dates[lang])
109                     lang = defaults.language;
110             }
111             o.language = lang;
112
113             switch(o.startView){
114                 case 2:
115                 case 'decade':
116                     o.startView = 2;
117                     break;
118                 case 1:
119                 case 'year':
120                     o.startView = 1;
121                     break;
122                 default:
123                     o.startView = 0;
124             }
125
126             switch (o.minViewMode) {
127                 case 1:
128                 case 'months':
129                     o.minViewMode = 1;
130                     break;
131                 case 2:
132                 case 'years':
133                     o.minViewMode = 2;
134                     break;
135                 default:
136                     o.minViewMode = 0;
137             }
138
139             o.startView = Math.max(o.startView, o.minViewMode);
140
141             o.weekStart %= 7;
142             o.weekEnd = ((o.weekStart + 6) % 7);
143
144             var format = DPGlobal.parseFormat(o.format);
145             if (o.startDate !== -Infinity) {
146                 if (!!o.startDate) {
147                     if (o.startDate instanceof Date)
148                         o.startDate = this._local_to_utc(this._zero_time(o.startDate));
149                     else
150                         o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
151                 } else {
152                     o.startDate = -Infinity;
153                 }
154             }
155             if (o.endDate !== Infinity) {
156                 if (!!o.endDate) {
157                     if (o.endDate instanceof Date)
158                         o.endDate = this._local_to_utc(this._zero_time(o.endDate));
159                     else
160                         o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
161                 } else {
162                     o.endDate = Infinity;
163                 }
164             }
165
166             o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
167             if (!$.isArray(o.daysOfWeekDisabled))
168                 o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
169             o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) {
170                 return parseInt(d, 10);
171             });
172
173             var plc = String(o.orientation).toLowerCase().split(/\s+/g),
174                 _plc = o.orientation.toLowerCase();
175             plc = $.grep(plc, function(word){
176                 return (/^auto|left|right|top|bottom$/).test(word);
177             });
178             o.orientation = {x: 'auto', y: 'auto'};
179             if (!_plc || _plc === 'auto')
180                 ; // no action
181             else if (plc.length === 1){
182                 switch(plc[0]){
183                     case 'top':
184                     case 'bottom':
185                         o.orientation.y = plc[0];
186                         break;
187                     case 'left':
188                     case 'right':
189                         o.orientation.x = plc[0];
190                         break;
191                 }
192             }
193             else {
194                 _plc = $.grep(plc, function(word){
195                     return (/^left|right$/).test(word);
196                 });
197                 o.orientation.x = _plc[0] || 'auto';
198
199                 _plc = $.grep(plc, function(word){
200                     return (/^top|bottom$/).test(word);
201                 });
202                 o.orientation.y = _plc[0] || 'auto';
203             }
204         },
205         _events: [],
206         _secondaryEvents: [],
207         _applyEvents: function(evs){
208             for (var i=0, el, ev; i<evs.length; i++){
209                 el = evs[i][0];
210                 ev = evs[i][1];
211                 el.on(ev);
212             }
213         },
214         _unapplyEvents: function(evs){
215             for (var i=0, el, ev; i<evs.length; i++){
216                 el = evs[i][0];
217                 ev = evs[i][1];
218                 el.off(ev);
219             }
220         },
221         _buildEvents: function(){
222             if (this.isInput) { // single input
223                 this._events = [
224                     [this.element, {
225                         focus: $.proxy(this.show, this),
226                         keyup: $.proxy(this.update, this),
227                         keydown: $.proxy(this.keydown, this)
228                     }]
229                 ];
230             }
231             else if (this.component && this.hasInput){ // component: input + button
232                 this._events = [
233                     // For components that are not readonly, allow keyboard nav
234                     [this.element.find('input'), {
235                         focus: $.proxy(this.show, this),
236                         keyup: $.proxy(this.update, this),
237                         keydown: $.proxy(this.keydown, this)
238                     }],
239                     [this.component, {
240                         click: $.proxy(this.show, this)
241                     }]
242                 ];
243             }
244             else if (this.element.is('div')) {  // inline datepicker
245                 this.isInline = true;
246             }
247             else {
248                 this._events = [
249                     [this.element, {
250                         click: $.proxy(this.show, this)
251                     }]
252                 ];
253             }
254
255             this._secondaryEvents = [
256                 [this.picker, {
257                     click: $.proxy(this.click, this)
258                 }],
259                 [$(window), {
260                     resize: $.proxy(this.place, this)
261                 }],
262                 [$(document), {
263                     mousedown: $.proxy(function (e) {
264                         // Clicked outside the datepicker, hide it
265                         if (!(
266                             this.element.is(e.target) ||
267                             this.element.find(e.target).length ||
268                             this.picker.is(e.target) ||
269                             this.picker.find(e.target).length
270                         )) {
271                             this.hide();
272                         }
273                     }, this)
274                 }]
275             ];
276         },
277         _attachEvents: function(){
278             this._detachEvents();
279             this._applyEvents(this._events);
280         },
281         _detachEvents: function(){
282             this._unapplyEvents(this._events);
283         },
284         _attachSecondaryEvents: function(){
285             this._detachSecondaryEvents();
286             this._applyEvents(this._secondaryEvents);
287         },
288         _detachSecondaryEvents: function(){
289             this._unapplyEvents(this._secondaryEvents);
290         },
291         _trigger: function(event, altdate){
292             var date = altdate || this.date,
293                 local_date = this._utc_to_local(date);
294
295             this.element.trigger({
296                 type: event,
297                 date: local_date,
298                 format: $.proxy(function(altformat){
299                     var format = altformat || this.o.format;
300                     return DPGlobal.formatDate(date, format, this.o.language);
301                 }, this)
302             });
303         },
304
305         show: function(e) {
306             if (!this.isInline)
307                 this.picker.appendTo('body');
308             this.picker.show();
309             this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
310             this.place();
311             this._attachSecondaryEvents();
312             if (e) {
313                 e.preventDefault();
314             }
315             this._trigger('show');
316         },
317
318         hide: function(e){
319             if(this.isInline) return;
320             if (!this.picker.is(':visible')) return;
321             this.picker.hide().detach();
322             this._detachSecondaryEvents();
323             this.viewMode = this.o.startView;
324             this.showMode();
325
326             if (
327                 this.o.forceParse &&
328                 (
329                     this.isInput && this.element.val() ||
330                     this.hasInput && this.element.find('input').val()
331                 )
332             )
333                 this.setValue();
334             this._trigger('hide');
335         },
336
337         remove: function() {
338             this.hide();
339             this._detachEvents();
340             this._detachSecondaryEvents();
341             this.picker.remove();
342             delete this.element.data().datepicker;
343             if (!this.isInput) {
344                 delete this.element.data().date;
345             }
346         },
347
348         _utc_to_local: function(utc){
349             return new Date(utc.getTime() + (utc.getTimezoneOffset()*60000));
350         },
351         _local_to_utc: function(local){
352             return new Date(local.getTime() - (local.getTimezoneOffset()*60000));
353         },
354         _zero_time: function(local){
355             return new Date(local.getFullYear(), local.getMonth(), local.getDate());
356         },
357         _zero_utc_time: function(utc){
358             return new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate()));
359         },
360
361         getDate: function() {
362             return this._utc_to_local(this.getUTCDate());
363         },
364
365         getUTCDate: function() {
366             return this.date;
367         },
368
369         setDate: function(d) {
370             this.setUTCDate(this._local_to_utc(d));
371         },
372
373         setUTCDate: function(d) {
374             this.date = d;
375             this.setValue();
376         },
377
378         setValue: function() {
379             var formatted = this.getFormattedDate();
380             if (!this.isInput) {
381                 if (this.component){
382                     this.element.find('input').val(formatted).change();
383                 }
384             } else {
385                 this.element.val(formatted).change();
386             }
387         },
388
389         getFormattedDate: function(format) {
390             if (format === undefined)
391                 format = this.o.format;
392             return DPGlobal.formatDate(this.date, format, this.o.language);
393         },
394
395         setStartDate: function(startDate){
396             this._process_options({startDate: startDate});
397             this.update();
398             this.updateNavArrows();
399         },
400
401         setEndDate: function(endDate){
402             this._process_options({endDate: endDate});
403             this.update();
404             this.updateNavArrows();
405         },
406
407         setDaysOfWeekDisabled: function(daysOfWeekDisabled){
408             this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
409             this.update();
410             this.updateNavArrows();
411         },
412
413         place: function(){
414                         if(this.isInline) return;
415             var calendarWidth = this.picker.outerWidth(),
416                 calendarHeight = this.picker.outerHeight(),
417                 visualPadding = 10,
418                 windowWidth = $window.width(),
419                 windowHeight = $window.height(),
420                 scrollTop = $window.scrollTop();
421
422             var zIndex = parseInt(this.element.parents().filter(function() {
423                             return $(this).css('z-index') != 'auto';
424                         }).first().css('z-index'))+10;
425             var offset = this.component ? this.component.parent().offset() : this.element.offset();
426             var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
427             var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
428             var left = offset.left,
429                 top = offset.top;
430
431             this.picker.removeClass(
432                 'datepicker-orient-top datepicker-orient-bottom '+
433                 'datepicker-orient-right datepicker-orient-left'
434             );
435
436             if (this.o.orientation.x !== 'auto') {
437                 this.picker.addClass('datepicker-orient-' + this.o.orientation.x);
438                 if (this.o.orientation.x === 'right')
439                     left -= calendarWidth - width;
440             }
441             // auto x orientation is best-placement: if it crosses a window
442             // edge, fudge it sideways
443             else {
444                 // Default to left
445                 this.picker.addClass('datepicker-orient-left');
446                 if (offset.left < 0)
447                     left -= offset.left - visualPadding;
448                 else if (offset.left + calendarWidth > windowWidth)
449                     left = windowWidth - calendarWidth - visualPadding;
450             }
451
452             // auto y orientation is best-situation: top or bottom, no fudging,
453             // decision based on which shows more of the calendar
454             var yorient = this.o.orientation.y,
455                 top_overflow, bottom_overflow;
456             if (yorient === 'auto') {
457                 top_overflow = -scrollTop + offset.top - calendarHeight;
458                 bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight);
459                 if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
460                     yorient = 'top';
461                 else
462                     yorient = 'bottom';
463             }
464             this.picker.addClass('datepicker-orient-' + yorient);
465             if (yorient === 'top')
466                 top += height;
467             else
468                 top -= calendarHeight + parseInt(this.picker.css('padding-top'));
469
470             this.picker.css({
471                 top: top,
472                 left: left,
473                 zIndex: zIndex
474             });
475         },
476
477         _allow_update: true,
478         update: function(){
479             if (!this._allow_update) return;
480
481             var oldDate = new Date(this.date),
482                 date, fromArgs = false;
483             if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
484                 date = arguments[0];
485                 if (date instanceof Date)
486                     date = this._local_to_utc(date);
487                 fromArgs = true;
488             } else {
489                 date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
490                 delete this.element.data().date;
491             }
492
493             this.date = DPGlobal.parseDate(date, this.o.format, this.o.language);
494
495             if (fromArgs) {
496                 // setting date by clicking
497                 this.setValue();
498             } else if (date) {
499                 // setting date by typing
500                 if (oldDate.getTime() !== this.date.getTime())
501                     this._trigger('changeDate');
502             } else {
503                 // clearing date
504                 this._trigger('clearDate');
505             }
506
507             if (this.date < this.o.startDate) {
508                 this.viewDate = new Date(this.o.startDate);
509                 this.date = new Date(this.o.startDate);
510             } else if (this.date > this.o.endDate) {
511                 this.viewDate = new Date(this.o.endDate);
512                 this.date = new Date(this.o.endDate);
513             } else {
514                 this.viewDate = new Date(this.date);
515                 this.date = new Date(this.date);
516             }
517             this.fill();
518         },
519
520         fillDow: function(){
521             var dowCnt = this.o.weekStart,
522             html = '<tr>';
523             if(this.o.calendarWeeks){
524                 var cell = '<th class="cw">&nbsp;</th>';
525                 html += cell;
526                 this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
527             }
528             while (dowCnt < this.o.weekStart + 7) {
529                 html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
530             }
531             html += '</tr>';
532             this.picker.find('.datepicker-days thead').append(html);
533         },
534
535         fillMonths: function(){
536             var html = '',
537             i = 0;
538             while (i < 12) {
539                 html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
540             }
541             this.picker.find('.datepicker-months td').html(html);
542         },
543
544         setRange: function(range){
545             if (!range || !range.length)
546                 delete this.range;
547             else
548                 this.range = $.map(range, function(d){ return d.valueOf(); });
549             this.fill();
550         },
551
552         getClassNames: function(date){
553             var cls = [],
554                 year = this.viewDate.getUTCFullYear(),
555                 month = this.viewDate.getUTCMonth(),
556                 currentDate = this.date.valueOf(),
557                 today = new Date();
558             if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) {
559                 cls.push('old');
560             } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) {
561                 cls.push('new');
562             }
563             // Compare internal UTC date with local today, not UTC today
564             if (this.o.todayHighlight &&
565                 date.getUTCFullYear() == today.getFullYear() &&
566                 date.getUTCMonth() == today.getMonth() &&
567                 date.getUTCDate() == today.getDate()) {
568                 cls.push('today');
569             }
570             if (currentDate && date.valueOf() == currentDate) {
571                 cls.push('active');
572             }
573             if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
574                 $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) {
575                 cls.push('disabled');
576             }
577             if (this.range){
578                 if (date > this.range[0] && date < this.range[this.range.length-1]){
579                     cls.push('range');
580                 }
581                 if ($.inArray(date.valueOf(), this.range) != -1){
582                     cls.push('selected');
583                 }
584             }
585             return cls;
586         },
587
588         fill: function() {
589             var d = new Date(this.viewDate),
590                 year = d.getUTCFullYear(),
591                 month = d.getUTCMonth(),
592                 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
593                 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
594                 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
595                 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
596                 currentDate = this.date && this.date.valueOf(),
597                 tooltip;
598             this.picker.find('.datepicker-days thead th.datepicker-switch')
599                         .text(dates[this.o.language].months[month]+' '+year);
600             this.picker.find('tfoot th.today')
601                         .text(dates[this.o.language].today)
602                         .toggle(this.o.todayBtn !== false);
603             this.picker.find('tfoot th.clear')
604                         .text(dates[this.o.language].clear)
605                         .toggle(this.o.clearBtn !== false);
606             this.updateNavArrows();
607             this.fillMonths();
608             var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
609                 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
610             prevMonth.setUTCDate(day);
611             prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
612             var nextMonth = new Date(prevMonth);
613             nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
614             nextMonth = nextMonth.valueOf();
615             var html = [];
616             var clsName;
617             while(prevMonth.valueOf() < nextMonth) {
618                 if (prevMonth.getUTCDay() == this.o.weekStart) {
619                     html.push('<tr>');
620                     if(this.o.calendarWeeks){
621                         // ISO 8601: First week contains first thursday.
622                         // ISO also states week starts on Monday, but we can be more abstract here.
623                         var
624                             // Start of current week: based on weekstart/current date
625                             ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
626                             // Thursday of this week
627                             th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
628                             // First Thursday of year, year from thursday
629                             yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
630                             // Calendar week: ms between thursdays, div ms per day, div 7 days
631                             calWeek =  (th - yth) / 864e5 / 7 + 1;
632                         html.push('<td class="cw">'+ calWeek +'</td>');
633
634                     }
635                 }
636                 clsName = this.getClassNames(prevMonth);
637                 clsName.push('day');
638
639                 if (this.o.beforeShowDay !== $.noop){
640                     var before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
641                     if (before === undefined)
642                         before = {};
643                     else if (typeof(before) === 'boolean')
644                         before = {enabled: before};
645                     else if (typeof(before) === 'string')
646                         before = {classes: before};
647                     if (before.enabled === false)
648                         clsName.push('disabled');
649                     if (before.classes)
650                         clsName = clsName.concat(before.classes.split(/\s+/));
651                     if (before.tooltip)
652                         tooltip = before.tooltip;
653                 }
654
655                 clsName = $.unique(clsName);
656                 html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
657                 if (prevMonth.getUTCDay() == this.o.weekEnd) {
658                     html.push('</tr>');
659                 }
660                 prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
661             }
662             this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
663             var currentYear = this.date && this.date.getUTCFullYear();
664
665             var months = this.picker.find('.datepicker-months')
666                         .find('th:eq(1)')
667                             .text(year)
668                             .end()
669                         .find('span').removeClass('active');
670             if (currentYear && currentYear == year) {
671                 months.eq(this.date.getUTCMonth()).addClass('active');
672             }
673             if (year < startYear || year > endYear) {
674                 months.addClass('disabled');
675             }
676             if (year == startYear) {
677                 months.slice(0, startMonth).addClass('disabled');
678             }
679             if (year == endYear) {
680                 months.slice(endMonth+1).addClass('disabled');
681             }
682
683             html = '';
684             year = parseInt(year/10, 10) * 10;
685             var yearCont = this.picker.find('.datepicker-years')
686                                 .find('th:eq(1)')
687                                     .text(year + '-' + (year + 9))
688                                     .end()
689                                 .find('td');
690             year -= 1;
691             for (var i = -1; i < 11; i++) {
692                 html += '<span class="year'+(i == -1 ? ' old' : i == 10 ? ' new' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
693                 year += 1;
694             }
695             yearCont.html(html);
696         },
697
698         updateNavArrows: function() {
699             if (!this._allow_update) return;
700
701             var d = new Date(this.viewDate),
702                 year = d.getUTCFullYear(),
703                 month = d.getUTCMonth();
704             switch (this.viewMode) {
705                 case 0:
706                     if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) {
707                         this.picker.find('.prev').css({visibility: 'hidden'});
708                     } else {
709                         this.picker.find('.prev').css({visibility: 'visible'});
710                     }
711                     if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) {
712                         this.picker.find('.next').css({visibility: 'hidden'});
713                     } else {
714                         this.picker.find('.next').css({visibility: 'visible'});
715                     }
716                     break;
717                 case 1:
718                 case 2:
719                     if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) {
720                         this.picker.find('.prev').css({visibility: 'hidden'});
721                     } else {
722                         this.picker.find('.prev').css({visibility: 'visible'});
723                     }
724                     if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) {
725                         this.picker.find('.next').css({visibility: 'hidden'});
726                     } else {
727                         this.picker.find('.next').css({visibility: 'visible'});
728                     }
729                     break;
730             }
731         },
732
733         click: function(e) {
734             e.preventDefault();
735             var target = $(e.target).closest('span, td, th');
736             if (target.length == 1) {
737                 switch(target[0].nodeName.toLowerCase()) {
738                     case 'th':
739                         switch(target[0].className) {
740                             case 'datepicker-switch':
741                                 this.showMode(1);
742                                 break;
743                             case 'prev':
744                             case 'next':
745                                 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
746                                 switch(this.viewMode){
747                                     case 0:
748                                         this.viewDate = this.moveMonth(this.viewDate, dir);
749                                         this._trigger('changeMonth', this.viewDate);
750                                         break;
751                                     case 1:
752                                     case 2:
753                                         this.viewDate = this.moveYear(this.viewDate, dir);
754                                         if (this.viewMode === 1)
755                                             this._trigger('changeYear', this.viewDate);
756                                         break;
757                                 }
758                                 this.fill();
759                                 break;
760                             case 'today':
761                                 var date = new Date();
762                                 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
763
764                                 this.showMode(-2);
765                                 var which = this.o.todayBtn == 'linked' ? null : 'view';
766                                 this._setDate(date, which);
767                                 break;
768                             case 'clear':
769                                 var element;
770                                 if (this.isInput)
771                                     element = this.element;
772                                 else if (this.component)
773                                     element = this.element.find('input');
774                                 if (element)
775                                     element.val("").change();
776                                 this._trigger('changeDate');
777                                 this.update();
778                                 if (this.o.autoclose)
779                                     this.hide();
780                                 break;
781                         }
782                         break;
783                     case 'span':
784                         if (!target.is('.disabled')) {
785                             this.viewDate.setUTCDate(1);
786                             if (target.is('.month')) {
787                                 var day = 1;
788                                 var month = target.parent().find('span').index(target);
789                                 var year = this.viewDate.getUTCFullYear();
790                                 this.viewDate.setUTCMonth(month);
791                                 this._trigger('changeMonth', this.viewDate);
792                                 if (this.o.minViewMode === 1) {
793                                     this._setDate(UTCDate(year, month, day,0,0,0,0));
794                                 }
795                             } else {
796                                 var year = parseInt(target.text(), 10)||0;
797                                 var day = 1;
798                                 var month = 0;
799                                 this.viewDate.setUTCFullYear(year);
800                                 this._trigger('changeYear', this.viewDate);
801                                 if (this.o.minViewMode === 2) {
802                                     this._setDate(UTCDate(year, month, day,0,0,0,0));
803                                 }
804                             }
805                             this.showMode(-1);
806                             this.fill();
807                         }
808                         break;
809                     case 'td':
810                         if (target.is('.day') && !target.is('.disabled')){
811                             var day = parseInt(target.text(), 10)||1;
812                             var year = this.viewDate.getUTCFullYear(),
813                                 month = this.viewDate.getUTCMonth();
814                             if (target.is('.old')) {
815                                 if (month === 0) {
816                                     month = 11;
817                                     year -= 1;
818                                 } else {
819                                     month -= 1;
820                                 }
821                             } else if (target.is('.new')) {
822                                 if (month == 11) {
823                                     month = 0;
824                                     year += 1;
825                                 } else {
826                                     month += 1;
827                                 }
828                             }
829                             this._setDate(UTCDate(year, month, day,0,0,0,0));
830                         }
831                         break;
832                 }
833             }
834         },
835
836         _setDate: function(date, which){
837             if (!which || which == 'date')
838                 this.date = new Date(date);
839             if (!which || which  == 'view')
840                 this.viewDate = new Date(date);
841             this.fill();
842             this.setValue();
843             this._trigger('changeDate');
844             var element;
845             if (this.isInput) {
846                 element = this.element;
847             } else if (this.component){
848                 element = this.element.find('input');
849             }
850             if (element) {
851                 element.change();
852             }
853             if (this.o.autoclose && (!which || which == 'date')) {
854                 this.hide();
855             }
856         },
857
858         moveMonth: function(date, dir){
859             if (!dir) return date;
860             var new_date = new Date(date.valueOf()),
861                 day = new_date.getUTCDate(),
862                 month = new_date.getUTCMonth(),
863                 mag = Math.abs(dir),
864                 new_month, test;
865             dir = dir > 0 ? 1 : -1;
866             if (mag == 1){
867                 test = dir == -1
868                     // If going back one month, make sure month is not current month
869                     // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
870                     ? function(){ return new_date.getUTCMonth() == month; }
871                     // If going forward one month, make sure month is as expected
872                     // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
873                     : function(){ return new_date.getUTCMonth() != new_month; };
874                 new_month = month + dir;
875                 new_date.setUTCMonth(new_month);
876                 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
877                 if (new_month < 0 || new_month > 11)
878                     new_month = (new_month + 12) % 12;
879             } else {
880                 // For magnitudes >1, move one month at a time...
881                 for (var i=0; i<mag; i++)
882                     // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
883                     new_date = this.moveMonth(new_date, dir);
884                 // ...then reset the day, keeping it in the new month
885                 new_month = new_date.getUTCMonth();
886                 new_date.setUTCDate(day);
887                 test = function(){ return new_month != new_date.getUTCMonth(); };
888             }
889             // Common date-resetting loop -- if date is beyond end of month, make it
890             // end of month
891             while (test()){
892                 new_date.setUTCDate(--day);
893                 new_date.setUTCMonth(new_month);
894             }
895             return new_date;
896         },
897
898         moveYear: function(date, dir){
899             return this.moveMonth(date, dir*12);
900         },
901
902         dateWithinRange: function(date){
903             return date >= this.o.startDate && date <= this.o.endDate;
904         },
905
906         keydown: function(e){
907             if (this.picker.is(':not(:visible)')){
908                 if (e.keyCode == 27) // allow escape to hide and re-show picker
909                     this.show();
910                 return;
911             }
912             var dateChanged = false,
913                 dir, day, month,
914                 newDate, newViewDate;
915             switch(e.keyCode){
916                 case 27: // escape
917                     this.hide();
918                     e.preventDefault();
919                     break;
920                 case 37: // left
921                 case 39: // right
922                     if (!this.o.keyboardNavigation) break;
923                     dir = e.keyCode == 37 ? -1 : 1;
924                     if (e.ctrlKey){
925                         newDate = this.moveYear(this.date, dir);
926                         newViewDate = this.moveYear(this.viewDate, dir);
927                         this._trigger('changeYear', this.viewDate);
928                     } else if (e.shiftKey){
929                         newDate = this.moveMonth(this.date, dir);
930                         newViewDate = this.moveMonth(this.viewDate, dir);
931                         this._trigger('changeMonth', this.viewDate);
932                     } else {
933                         newDate = new Date(this.date);
934                         newDate.setUTCDate(this.date.getUTCDate() + dir);
935                         newViewDate = new Date(this.viewDate);
936                         newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
937                     }
938                     if (this.dateWithinRange(newDate)){
939                         this.date = newDate;
940                         this.viewDate = newViewDate;
941                         this.setValue();
942                         this.update();
943                         e.preventDefault();
944                         dateChanged = true;
945                     }
946                     break;
947                 case 38: // up
948                 case 40: // down
949                     if (!this.o.keyboardNavigation) break;
950                     dir = e.keyCode == 38 ? -1 : 1;
951                     if (e.ctrlKey){
952                         newDate = this.moveYear(this.date, dir);
953                         newViewDate = this.moveYear(this.viewDate, dir);
954                         this._trigger('changeYear', this.viewDate);
955                     } else if (e.shiftKey){
956                         newDate = this.moveMonth(this.date, dir);
957                         newViewDate = this.moveMonth(this.viewDate, dir);
958                         this._trigger('changeMonth', this.viewDate);
959                     } else {
960                         newDate = new Date(this.date);
961                         newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
962                         newViewDate = new Date(this.viewDate);
963                         newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
964                     }
965                     if (this.dateWithinRange(newDate)){
966                         this.date = newDate;
967                         this.viewDate = newViewDate;
968                         this.setValue();
969                         this.update();
970                         e.preventDefault();
971                         dateChanged = true;
972                     }
973                     break;
974                 case 13: // enter
975                     this.hide();
976                     e.preventDefault();
977                     break;
978                 case 9: // tab
979                     this.hide();
980                     break;
981             }
982             if (dateChanged){
983                 this._trigger('changeDate');
984                 var element;
985                 if (this.isInput) {
986                     element = this.element;
987                 } else if (this.component){
988                     element = this.element.find('input');
989                 }
990                 if (element) {
991                     element.change();
992                 }
993             }
994         },
995
996         showMode: function(dir) {
997             if (dir) {
998                 this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
999             }
1000             /*
1001                 vitalets: fixing bug of very special conditions:
1002                 jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
1003                 Method show() does not set display css correctly and datepicker is not shown.
1004                 Changed to .css('display', 'block') solve the problem.
1005                 See https://github.com/vitalets/x-editable/issues/37
1006
1007                 In jquery 1.7.2+ everything works fine.
1008             */
1009             //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
1010             this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
1011             this.updateNavArrows();
1012         }
1013     };
1014
1015     var DateRangePicker = function(element, options){
1016         this.element = $(element);
1017         this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; });
1018         delete options.inputs;
1019
1020         $(this.inputs)
1021             .datepicker(options)
1022             .bind('changeDate', $.proxy(this.dateUpdated, this));
1023
1024         this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); });
1025         this.updateDates();
1026     };
1027     DateRangePicker.prototype = {
1028         updateDates: function(){
1029             this.dates = $.map(this.pickers, function(i){ return i.date; });
1030             this.updateRanges();
1031         },
1032         updateRanges: function(){
1033             var range = $.map(this.dates, function(d){ return d.valueOf(); });
1034             $.each(this.pickers, function(i, p){
1035                 p.setRange(range);
1036             });
1037         },
1038         dateUpdated: function(e){
1039             var dp = $(e.target).data('datepicker'),
1040                 new_date = dp.getUTCDate(),
1041                 i = $.inArray(e.target, this.inputs),
1042                 l = this.inputs.length;
1043             if (i == -1) return;
1044
1045             if (new_date < this.dates[i]){
1046                 // Date being moved earlier/left
1047                 while (i>=0 && new_date < this.dates[i]){
1048                     this.pickers[i--].setUTCDate(new_date);
1049                 }
1050             }
1051             else if (new_date > this.dates[i]){
1052                 // Date being moved later/right
1053                 while (i<l && new_date > this.dates[i]){
1054                     this.pickers[i++].setUTCDate(new_date);
1055                 }
1056             }
1057             this.updateDates();
1058         },
1059         remove: function(){
1060             $.map(this.pickers, function(p){ p.remove(); });
1061             delete this.element.data().datepicker;
1062         }
1063     };
1064
1065     function opts_from_el(el, prefix){
1066         // Derive options from element data-attrs
1067         var data = $(el).data(),
1068             out = {}, inkey,
1069             replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'),
1070             prefix = new RegExp('^' + prefix.toLowerCase());
1071         for (var key in data)
1072             if (prefix.test(key)){
1073                 inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); });
1074                 out[inkey] = data[key];
1075             }
1076         return out;
1077     }
1078
1079     function opts_from_locale(lang){
1080         // Derive options from locale plugins
1081         var out = {};
1082         // Check if "de-DE" style date is available, if not language should
1083         // fallback to 2 letter code eg "de"
1084         if (!dates[lang]) {
1085             lang = lang.split('-')[0]
1086             if (!dates[lang])
1087                 return;
1088         }
1089         var d = dates[lang];
1090         $.each(locale_opts, function(i,k){
1091             if (k in d)
1092                 out[k] = d[k];
1093         });
1094         return out;
1095     }
1096
1097     var old = $.fn.datepicker;
1098     $.fn.datepicker = function ( option ) {
1099         var args = Array.apply(null, arguments);
1100         args.shift();
1101         var internal_return,
1102             this_return;
1103         this.each(function () {
1104             var $this = $(this),
1105                 data = $this.data('datepicker'),
1106                 options = typeof option == 'object' && option;
1107             if (!data) {
1108                 var elopts = opts_from_el(this, 'date'),
1109                     // Preliminary otions
1110                     xopts = $.extend({}, defaults, elopts, options),
1111                     locopts = opts_from_locale(xopts.language),
1112                     // Options priority: js args, data-attrs, locales, defaults
1113                     opts = $.extend({}, defaults, locopts, elopts, options);
1114                 if ($this.is('.input-daterange') || opts.inputs){
1115                     var ropts = {
1116                         inputs: opts.inputs || $this.find('input').toArray()
1117                     };
1118                     $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
1119                 }
1120                 else{
1121                     $this.data('datepicker', (data = new Datepicker(this, opts)));
1122                 }
1123             }
1124             if (typeof option == 'string' && typeof data[option] == 'function') {
1125                 internal_return = data[option].apply(data, args);
1126                 if (internal_return !== undefined)
1127                     return false;
1128             }
1129         });
1130         if (internal_return !== undefined)
1131             return internal_return;
1132         else
1133             return this;
1134     };
1135
1136     var defaults = $.fn.datepicker.defaults = {
1137         autoclose: false,
1138         beforeShowDay: $.noop,
1139         calendarWeeks: false,
1140         clearBtn: false,
1141         daysOfWeekDisabled: [],
1142         endDate: Infinity,
1143         forceParse: true,
1144         format: 'mm/dd/yyyy',
1145         keyboardNavigation: true,
1146         language: 'en',
1147         minViewMode: 0,
1148         orientation: "auto",
1149         rtl: false,
1150         startDate: -Infinity,
1151         startView: 0,
1152         todayBtn: false,
1153         todayHighlight: false,
1154         weekStart: 0
1155     };
1156     var locale_opts = $.fn.datepicker.locale_opts = [
1157         'format',
1158         'rtl',
1159         'weekStart'
1160     ];
1161     $.fn.datepicker.Constructor = Datepicker;
1162     var dates = $.fn.datepicker.dates = {
1163         en: {
1164             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
1165             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1166             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
1167             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1168             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1169             today: "Today",
1170             clear: "Clear"
1171         }
1172     };
1173
1174     var DPGlobal = {
1175         modes: [
1176             {
1177                 clsName: 'days',
1178                 navFnc: 'Month',
1179                 navStep: 1
1180             },
1181             {
1182                 clsName: 'months',
1183                 navFnc: 'FullYear',
1184                 navStep: 1
1185             },
1186             {
1187                 clsName: 'years',
1188                 navFnc: 'FullYear',
1189                 navStep: 10
1190         }],
1191         isLeapYear: function (year) {
1192             return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
1193         },
1194         getDaysInMonth: function (year, month) {
1195             return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
1196         },
1197         validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
1198         nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
1199         parseFormat: function(format){
1200             // IE treats \0 as a string end in inputs (truncating the value),
1201             // so it's a bad format delimiter, anyway
1202             var separators = format.replace(this.validParts, '\0').split('\0'),
1203                 parts = format.match(this.validParts);
1204             if (!separators || !separators.length || !parts || parts.length === 0){
1205                 throw new Error("Invalid date format.");
1206             }
1207             return {separators: separators, parts: parts};
1208         },
1209         parseDate: function(date, format, language) {
1210             if (date instanceof Date) return date;
1211             if (typeof format === 'string')
1212                 format = DPGlobal.parseFormat(format);
1213             if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
1214                 var part_re = /([\-+]\d+)([dmwy])/,
1215                     parts = date.match(/([\-+]\d+)([dmwy])/g),
1216                     part, dir;
1217                 date = new Date();
1218                 for (var i=0; i<parts.length; i++) {
1219                     part = part_re.exec(parts[i]);
1220                     dir = parseInt(part[1]);
1221                     switch(part[2]){
1222                         case 'd':
1223                             date.setUTCDate(date.getUTCDate() + dir);
1224                             break;
1225                         case 'm':
1226                             date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
1227                             break;
1228                         case 'w':
1229                             date.setUTCDate(date.getUTCDate() + dir * 7);
1230                             break;
1231                         case 'y':
1232                             date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
1233                             break;
1234                     }
1235                 }
1236                 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
1237             }
1238             var parts = date && date.match(this.nonpunctuation) || [],
1239                 date = new Date(),
1240                 parsed = {},
1241                 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
1242                 setters_map = {
1243                     yyyy: function(d,v){ return d.setUTCFullYear(v); },
1244                     yy: function(d,v){ return d.setUTCFullYear(2000+v); },
1245                     m: function(d,v){
1246                         if (isNaN(d))
1247                             return d;
1248                         v -= 1;
1249                         while (v<0) v += 12;
1250                         v %= 12;
1251                         d.setUTCMonth(v);
1252                         while (d.getUTCMonth() != v)
1253                             d.setUTCDate(d.getUTCDate()-1);
1254                         return d;
1255                     },
1256                     d: function(d,v){ return d.setUTCDate(v); }
1257                 },
1258                 val, filtered, part;
1259             setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1260             setters_map['dd'] = setters_map['d'];
1261             date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
1262             var fparts = format.parts.slice();
1263             // Remove noop parts
1264             if (parts.length != fparts.length) {
1265                 fparts = $(fparts).filter(function(i,p){
1266                     return $.inArray(p, setters_order) !== -1;
1267                 }).toArray();
1268             }
1269             // Process remainder
1270             if (parts.length == fparts.length) {
1271                 for (var i=0, cnt = fparts.length; i < cnt; i++) {
1272                     val = parseInt(parts[i], 10);
1273                     part = fparts[i];
1274                     if (isNaN(val)) {
1275                         switch(part) {
1276                             case 'MM':
1277                                 filtered = $(dates[language].months).filter(function(){
1278                                     var m = this.slice(0, parts[i].length),
1279                                         p = parts[i].slice(0, m.length);
1280                                     return m == p;
1281                                 });
1282                                 val = $.inArray(filtered[0], dates[language].months) + 1;
1283                                 break;
1284                             case 'M':
1285                                 filtered = $(dates[language].monthsShort).filter(function(){
1286                                     var m = this.slice(0, parts[i].length),
1287                                         p = parts[i].slice(0, m.length);
1288                                     return m == p;
1289                                 });
1290                                 val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1291                                 break;
1292                         }
1293                     }
1294                     parsed[part] = val;
1295                 }
1296                 for (var i=0, _date, s; i<setters_order.length; i++){
1297                     s = setters_order[i];
1298                     if (s in parsed && !isNaN(parsed[s])){
1299                         _date = new Date(date);
1300                         setters_map[s](_date, parsed[s]);
1301                         if (!isNaN(_date))
1302                             date = _date;
1303                     }
1304                 }
1305             }
1306             return date;
1307         },
1308         formatDate: function(date, format, language){
1309             if (typeof format === 'string')
1310                 format = DPGlobal.parseFormat(format);
1311             var val = {
1312                 d: date.getUTCDate(),
1313                 D: dates[language].daysShort[date.getUTCDay()],
1314                 DD: dates[language].days[date.getUTCDay()],
1315                 m: date.getUTCMonth() + 1,
1316                 M: dates[language].monthsShort[date.getUTCMonth()],
1317                 MM: dates[language].months[date.getUTCMonth()],
1318                 yy: date.getUTCFullYear().toString().substring(2),
1319                 yyyy: date.getUTCFullYear()
1320             };
1321             val.dd = (val.d < 10 ? '0' : '') + val.d;
1322             val.mm = (val.m < 10 ? '0' : '') + val.m;
1323             var date = [],
1324                 seps = $.extend([], format.separators);
1325             for (var i=0, cnt = format.parts.length; i <= cnt; i++) {
1326                 if (seps.length)
1327                     date.push(seps.shift());
1328                 date.push(val[format.parts[i]]);
1329             }
1330             return date.join('');
1331         },
1332         headTemplate: '<thead>'+
1333                             '<tr>'+
1334                                 '<th class="prev"><i class="fa fa-angle-left"></i></th>'+
1335                                 '<th colspan="5" class="datepicker-switch"></th>'+
1336                                 '<th class="next"><i class="fa fa-angle-right"></i></th>'+
1337                             '</tr>'+
1338                         '</thead>',
1339         contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
1340         footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>'
1341     };
1342     DPGlobal.template = '<div class="datepicker">'+
1343                             '<div class="datepicker-days">'+
1344                                 '<table class=" table-condensed">'+
1345                                     DPGlobal.headTemplate+
1346                                     '<tbody></tbody>'+
1347                                     DPGlobal.footTemplate+
1348                                 '</table>'+
1349                             '</div>'+
1350                             '<div class="datepicker-months">'+
1351                                 '<table class="table-condensed">'+
1352                                     DPGlobal.headTemplate+
1353                                     DPGlobal.contTemplate+
1354                                     DPGlobal.footTemplate+
1355                                 '</table>'+
1356                             '</div>'+
1357                             '<div class="datepicker-years">'+
1358                                 '<table class="table-condensed">'+
1359                                     DPGlobal.headTemplate+
1360                                     DPGlobal.contTemplate+
1361                                     DPGlobal.footTemplate+
1362                                 '</table>'+
1363                             '</div>'+
1364                         '</div>';
1365
1366     $.fn.datepicker.DPGlobal = DPGlobal;
1367
1368
1369     /* DATEPICKER NO CONFLICT
1370     * =================== */
1371
1372     $.fn.datepicker.noConflict = function(){
1373         $.fn.datepicker = old;
1374         return this;
1375     };
1376
1377
1378     /* DATEPICKER DATA-API
1379     * ================== */
1380
1381     $(document).on(
1382         'focus.datepicker.data-api click.datepicker.data-api',
1383         '[data-provide="datepicker"]',
1384         function(e){
1385             var $this = $(this);
1386             if ($this.data('datepicker')) return;
1387             e.preventDefault();
1388             // component click requires us to explicitly show it
1389             $this.datepicker('show');
1390         }
1391     );
1392     $(function(){
1393         $('[data-provide="datepicker-inline"]').datepicker();
1394     });
1395
1396 }( window.jQuery ));