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