]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/datepicker/bootstrap-datepicker.js
LP#1778972: add a couple more aria-hidden
[working/Evergreen.git] / Open-ILS / web / js / datepicker / bootstrap-datepicker.js
1 /*!
2  * Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
3  *
4  * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
5  */
6
7 (function(factory){
8     if (typeof define === 'function' && define.amd) {
9         define(['jquery'], factory);
10     } else if (typeof exports === 'object') {
11         factory(require('jquery'));
12     } else {
13         factory(jQuery);
14     }
15 }(function($, undefined){
16         function UTCDate(){
17                 return new Date(Date.UTC.apply(Date, arguments));
18         }
19         function UTCToday(){
20                 var today = new Date();
21                 return UTCDate(today.getFullYear(), today.getMonth(), today.getDate());
22         }
23         function isUTCEquals(date1, date2) {
24                 return (
25                         date1.getUTCFullYear() === date2.getUTCFullYear() &&
26                         date1.getUTCMonth() === date2.getUTCMonth() &&
27                         date1.getUTCDate() === date2.getUTCDate()
28                 );
29         }
30         function alias(method, deprecationMsg){
31                 return function(){
32                         if (deprecationMsg !== undefined) {
33                                 $.fn.datepicker.deprecated(deprecationMsg);
34                         }
35
36                         return this[method].apply(this, arguments);
37                 };
38         }
39         function isValidDate(d) {
40                 return d && !isNaN(d.getTime());
41         }
42
43         var DateArray = (function(){
44                 var extras = {
45                         get: function(i){
46                                 return this.slice(i)[0];
47                         },
48                         contains: function(d){
49                                 // Array.indexOf is not cross-browser;
50                                 // $.inArray doesn't work with Dates
51                                 var val = d && d.valueOf();
52                                 for (var i=0, l=this.length; i < l; i++)
53           // Use date arithmetic to allow dates with different times to match
54           if (0 <= this[i].valueOf() - val && this[i].valueOf() - val < 1000*60*60*24)
55                                                 return i;
56                                 return -1;
57                         },
58                         remove: function(i){
59                                 this.splice(i,1);
60                         },
61                         replace: function(new_array){
62                                 if (!new_array)
63                                         return;
64                                 if (!$.isArray(new_array))
65                                         new_array = [new_array];
66                                 this.clear();
67                                 this.push.apply(this, new_array);
68                         },
69                         clear: function(){
70                                 this.length = 0;
71                         },
72                         copy: function(){
73                                 var a = new DateArray();
74                                 a.replace(this);
75                                 return a;
76                         }
77                 };
78
79                 return function(){
80                         var a = [];
81                         a.push.apply(a, arguments);
82                         $.extend(a, extras);
83                         return a;
84                 };
85         })();
86
87
88         // Picker object
89
90         var Datepicker = function(element, options){
91                 $.data(element, 'datepicker', this);
92
93                 this._events = [];
94                 this._secondaryEvents = [];
95
96                 this._process_options(options);
97
98                 this.dates = new DateArray();
99                 this.viewDate = this.o.defaultViewDate;
100                 this.focusDate = null;
101
102                 this.element = $(element);
103                 this.isInput = this.element.is('input');
104                 this.inputField = this.isInput ? this.element : this.element.find('input');
105                 this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .input-group-append, .input-group-prepend, .btn') : false;
106                 if (this.component && this.component.length === 0)
107                         this.component = false;
108                 this.isInline = !this.component && this.element.is('div');
109
110                 this.picker = $(DPGlobal.template);
111
112                 // Checking templates and inserting
113                 if (this._check_template(this.o.templates.leftArrow)) {
114                         this.picker.find('.prev').html(this.o.templates.leftArrow);
115                 }
116
117                 if (this._check_template(this.o.templates.rightArrow)) {
118                         this.picker.find('.next').html(this.o.templates.rightArrow);
119                 }
120
121                 this._buildEvents();
122                 this._attachEvents();
123
124                 if (this.isInline){
125                         this.picker.addClass('datepicker-inline').appendTo(this.element);
126                 }
127                 else {
128                         this.picker.addClass('datepicker-dropdown dropdown-menu');
129                 }
130
131                 if (this.o.rtl){
132                         this.picker.addClass('datepicker-rtl');
133                 }
134
135                 if (this.o.calendarWeeks) {
136                         this.picker.find('.datepicker-days .datepicker-switch, thead .datepicker-title, tfoot .today, tfoot .clear')
137                                 .attr('colspan', function(i, val){
138                                         return Number(val) + 1;
139                                 });
140                 }
141
142                 this._process_options({
143                         startDate: this._o.startDate,
144                         endDate: this._o.endDate,
145                         daysOfWeekDisabled: this.o.daysOfWeekDisabled,
146                         daysOfWeekHighlighted: this.o.daysOfWeekHighlighted,
147                         datesDisabled: this.o.datesDisabled
148                 });
149
150                 this._allow_update = false;
151                 this.setViewMode(this.o.startView);
152                 this._allow_update = true;
153
154                 this.fillDow();
155                 this.fillMonths();
156
157                 this.update();
158
159                 if (this.isInline){
160                         this.show();
161                 }
162         };
163
164         Datepicker.prototype = {
165                 constructor: Datepicker,
166
167                 _resolveViewName: function(view){
168                         $.each(DPGlobal.viewModes, function(i, viewMode){
169                                 if (view === i || $.inArray(view, viewMode.names) !== -1){
170                                         view = i;
171                                         return false;
172                                 }
173                         });
174
175                         return view;
176                 },
177
178                 _resolveDaysOfWeek: function(daysOfWeek){
179                         if (!$.isArray(daysOfWeek))
180                                 daysOfWeek = daysOfWeek.split(/[,\s]*/);
181                         return $.map(daysOfWeek, Number);
182                 },
183
184                 _check_template: function(tmp){
185                         try {
186                                 // If empty
187                                 if (tmp === undefined || tmp === "") {
188                                         return false;
189                                 }
190                                 // If no html, everything ok
191                                 if ((tmp.match(/[<>]/g) || []).length <= 0) {
192                                         return true;
193                                 }
194                                 // Checking if html is fine
195                                 var jDom = $(tmp);
196                                 return jDom.length > 0;
197                         }
198                         catch (ex) {
199                                 return false;
200                         }
201                 },
202
203                 _process_options: function(opts){
204                         // Store raw options for reference
205                         this._o = $.extend({}, this._o, opts);
206                         // Processed options
207                         var o = this.o = $.extend({}, this._o);
208
209                         // Check if "de-DE" style date is available, if not language should
210                         // fallback to 2 letter code eg "de"
211                         var lang = o.language;
212                         if (!dates[lang]){
213                                 lang = lang.split('-')[0];
214                                 if (!dates[lang])
215                                         lang = defaults.language;
216                         }
217                         o.language = lang;
218
219                         // Retrieve view index from any aliases
220                         o.startView = this._resolveViewName(o.startView);
221                         o.minViewMode = this._resolveViewName(o.minViewMode);
222                         o.maxViewMode = this._resolveViewName(o.maxViewMode);
223
224                         // Check view is between min and max
225                         o.startView = Math.max(this.o.minViewMode, Math.min(this.o.maxViewMode, o.startView));
226
227                         // true, false, or Number > 0
228                         if (o.multidate !== true){
229                                 o.multidate = Number(o.multidate) || false;
230                                 if (o.multidate !== false)
231                                         o.multidate = Math.max(0, o.multidate);
232                         }
233                         o.multidateSeparator = String(o.multidateSeparator);
234
235                         o.weekStart %= 7;
236                         o.weekEnd = (o.weekStart + 6) % 7;
237
238                         var format = DPGlobal.parseFormat(o.format);
239                         if (o.startDate !== -Infinity){
240                                 if (!!o.startDate){
241                                         if (o.startDate instanceof Date)
242                                                 o.startDate = this._local_to_utc(this._zero_time(o.startDate));
243                                         else
244                                                 o.startDate = DPGlobal.parseDate(o.startDate, format, o.language, o.assumeNearbyYear);
245                                 }
246                                 else {
247                                         o.startDate = -Infinity;
248                                 }
249                         }
250                         if (o.endDate !== Infinity){
251                                 if (!!o.endDate){
252                                         if (o.endDate instanceof Date)
253                                                 o.endDate = this._local_to_utc(this._zero_time(o.endDate));
254                                         else
255                                                 o.endDate = DPGlobal.parseDate(o.endDate, format, o.language, o.assumeNearbyYear);
256                                 }
257                                 else {
258                                         o.endDate = Infinity;
259                                 }
260                         }
261
262                         o.daysOfWeekDisabled = this._resolveDaysOfWeek(o.daysOfWeekDisabled||[]);
263                         o.daysOfWeekHighlighted = this._resolveDaysOfWeek(o.daysOfWeekHighlighted||[]);
264
265                         o.datesDisabled = o.datesDisabled||[];
266                         if (!$.isArray(o.datesDisabled)) {
267                                 o.datesDisabled = o.datesDisabled.split(',');
268                         }
269                         o.datesDisabled = $.map(o.datesDisabled, function(d){
270                                 return DPGlobal.parseDate(d, format, o.language, o.assumeNearbyYear);
271                         });
272
273                         var plc = String(o.orientation).toLowerCase().split(/\s+/g),
274                                 _plc = o.orientation.toLowerCase();
275                         plc = $.grep(plc, function(word){
276                                 return /^auto|left|right|top|bottom$/.test(word);
277                         });
278                         o.orientation = {x: 'auto', y: 'auto'};
279                         if (!_plc || _plc === 'auto')
280                                 ; // no action
281                         else if (plc.length === 1){
282                                 switch (plc[0]){
283                                         case 'top':
284                                         case 'bottom':
285                                                 o.orientation.y = plc[0];
286                                                 break;
287                                         case 'left':
288                                         case 'right':
289                                                 o.orientation.x = plc[0];
290                                                 break;
291                                 }
292                         }
293                         else {
294                                 _plc = $.grep(plc, function(word){
295                                         return /^left|right$/.test(word);
296                                 });
297                                 o.orientation.x = _plc[0] || 'auto';
298
299                                 _plc = $.grep(plc, function(word){
300                                         return /^top|bottom$/.test(word);
301                                 });
302                                 o.orientation.y = _plc[0] || 'auto';
303                         }
304                         if (o.defaultViewDate instanceof Date || typeof o.defaultViewDate === 'string') {
305                                 o.defaultViewDate = DPGlobal.parseDate(o.defaultViewDate, format, o.language, o.assumeNearbyYear);
306                         } else if (o.defaultViewDate) {
307                                 var year = o.defaultViewDate.year || new Date().getFullYear();
308                                 var month = o.defaultViewDate.month || 0;
309                                 var day = o.defaultViewDate.day || 1;
310                                 o.defaultViewDate = UTCDate(year, month, day);
311                         } else {
312                                 o.defaultViewDate = UTCToday();
313                         }
314                 },
315                 _applyEvents: function(evs){
316                         for (var i=0, el, ch, ev; i < evs.length; i++){
317                                 el = evs[i][0];
318                                 if (evs[i].length === 2){
319                                         ch = undefined;
320                                         ev = evs[i][1];
321                                 } else if (evs[i].length === 3){
322                                         ch = evs[i][1];
323                                         ev = evs[i][2];
324                                 }
325                                 el.on(ev, ch);
326                         }
327                 },
328                 _unapplyEvents: function(evs){
329                         for (var i=0, el, ev, ch; i < evs.length; i++){
330                                 el = evs[i][0];
331                                 if (evs[i].length === 2){
332                                         ch = undefined;
333                                         ev = evs[i][1];
334                                 } else if (evs[i].length === 3){
335                                         ch = evs[i][1];
336                                         ev = evs[i][2];
337                                 }
338                                 el.off(ev, ch);
339                         }
340                 },
341                 _buildEvents: function(){
342             var events = {
343                 keyup: $.proxy(function(e){
344                     if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1)
345                         this.update();
346                 }, this),
347                 keydown: $.proxy(this.keydown, this),
348                 paste: $.proxy(this.paste, this)
349             };
350
351             if (this.o.showOnFocus === true) {
352                 events.focus = $.proxy(this.show, this);
353             }
354
355             if (this.isInput) { // single input
356                 this._events = [
357                     [this.element, events]
358                 ];
359             }
360             // component: input + button
361             else if (this.component && this.inputField.length) {
362                 this._events = [
363                     // For components that are not readonly, allow keyboard nav
364                     [this.inputField, events],
365                     [this.component, {
366                         click: $.proxy(this.show, this)
367                     }]
368                 ];
369             }
370                         else {
371                                 this._events = [
372                                         [this.element, {
373                                                 click: $.proxy(this.show, this),
374                                                 keydown: $.proxy(this.keydown, this)
375                                         }]
376                                 ];
377                         }
378                         this._events.push(
379                                 // Component: listen for blur on element descendants
380                                 [this.element, '*', {
381                                         blur: $.proxy(function(e){
382                                                 this._focused_from = e.target;
383                                         }, this)
384                                 }],
385                                 // Input: listen for blur on element
386                                 [this.element, {
387                                         blur: $.proxy(function(e){
388                                                 this._focused_from = e.target;
389                                         }, this)
390                                 }]
391                         );
392
393                         if (this.o.immediateUpdates) {
394                                 // Trigger input updates immediately on changed year/month
395                                 this._events.push([this.element, {
396                                         'changeYear changeMonth': $.proxy(function(e){
397                                                 this.update(e.date);
398                                         }, this)
399                                 }]);
400                         }
401
402                         this._secondaryEvents = [
403                                 [this.picker, {
404                                         click: $.proxy(this.click, this)
405                                 }],
406                                 [this.picker, '.prev, .next', {
407                                         click: $.proxy(this.navArrowsClick, this)
408                                 }],
409                                 [this.picker, '.day:not(.disabled)', {
410                                         click: $.proxy(this.dayCellClick, this)
411                                 }],
412                                 [$(window), {
413                                         resize: $.proxy(this.place, this)
414                                 }],
415                                 [$(document), {
416                                         'mousedown touchstart': $.proxy(function(e){
417                                                 // Clicked outside the datepicker, hide it
418                                                 if (!(
419                                                         this.element.is(e.target) ||
420                                                         this.element.find(e.target).length ||
421                                                         this.picker.is(e.target) ||
422                                                         this.picker.find(e.target).length ||
423                                                         this.isInline
424                                                 )){
425                                                         this.hide();
426                                                 }
427                                         }, this)
428                                 }]
429                         ];
430                 },
431                 _attachEvents: function(){
432                         this._detachEvents();
433                         this._applyEvents(this._events);
434                 },
435                 _detachEvents: function(){
436                         this._unapplyEvents(this._events);
437                 },
438                 _attachSecondaryEvents: function(){
439                         this._detachSecondaryEvents();
440                         this._applyEvents(this._secondaryEvents);
441                 },
442                 _detachSecondaryEvents: function(){
443                         this._unapplyEvents(this._secondaryEvents);
444                 },
445                 _trigger: function(event, altdate){
446                         var date = altdate || this.dates.get(-1),
447                                 local_date = this._utc_to_local(date);
448
449                         this.element.trigger({
450                                 type: event,
451                                 date: local_date,
452                                 viewMode: this.viewMode,
453                                 dates: $.map(this.dates, this._utc_to_local),
454                                 format: $.proxy(function(ix, format){
455                                         if (arguments.length === 0){
456                                                 ix = this.dates.length - 1;
457                                                 format = this.o.format;
458                                         } else if (typeof ix === 'string'){
459                                                 format = ix;
460                                                 ix = this.dates.length - 1;
461                                         }
462                                         format = format || this.o.format;
463                                         var date = this.dates.get(ix);
464                                         return DPGlobal.formatDate(date, format, this.o.language);
465                                 }, this)
466                         });
467                 },
468
469                 show: function(){
470                         if (this.inputField.is(':disabled') || (this.inputField.prop('readonly') && this.o.enableOnReadonly === false))
471                                 return;
472                         if (!this.isInline)
473                                 this.picker.appendTo(this.o.container);
474                         this.place();
475                         this.picker.show();
476                         this._attachSecondaryEvents();
477                         this._trigger('show');
478                         if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) {
479                                 $(this.element).blur();
480                         }
481                         return this;
482                 },
483
484                 hide: function(){
485                         if (this.isInline || !this.picker.is(':visible'))
486                                 return this;
487                         this.focusDate = null;
488                         this.picker.hide().detach();
489                         this._detachSecondaryEvents();
490                         this.setViewMode(this.o.startView);
491
492                         if (this.o.forceParse && this.inputField.val())
493                                 this.setValue();
494                         this._trigger('hide');
495                         return this;
496                 },
497
498                 destroy: function(){
499                         this.hide();
500                         this._detachEvents();
501                         this._detachSecondaryEvents();
502                         this.picker.remove();
503                         delete this.element.data().datepicker;
504                         if (!this.isInput){
505                                 delete this.element.data().date;
506                         }
507                         return this;
508                 },
509
510                 paste: function(e){
511                         var dateString;
512                         if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.types
513                                 && $.inArray('text/plain', e.originalEvent.clipboardData.types) !== -1) {
514                                 dateString = e.originalEvent.clipboardData.getData('text/plain');
515                         } else if (window.clipboardData) {
516                                 dateString = window.clipboardData.getData('Text');
517                         } else {
518                                 return;
519                         }
520                         this.setDate(dateString);
521                         this.update();
522                         e.preventDefault();
523                 },
524
525                 _utc_to_local: function(utc){
526                         if (!utc) {
527                                 return utc;
528                         }
529
530                         var local = new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
531
532                         if (local.getTimezoneOffset() !== utc.getTimezoneOffset()) {
533                                 local = new Date(utc.getTime() + (local.getTimezoneOffset() * 60000));
534                         }
535
536                         return local;
537                 },
538                 _local_to_utc: function(local){
539                         return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000));
540                 },
541                 _zero_time: function(local){
542                         return local && new Date(local.getFullYear(), local.getMonth(), local.getDate());
543                 },
544                 _zero_utc_time: function(utc){
545                         return utc && UTCDate(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
546                 },
547
548                 getDates: function(){
549                         return $.map(this.dates, this._utc_to_local);
550                 },
551
552                 getUTCDates: function(){
553                         return $.map(this.dates, function(d){
554                                 return new Date(d);
555                         });
556                 },
557
558                 getDate: function(){
559                         return this._utc_to_local(this.getUTCDate());
560                 },
561
562                 getUTCDate: function(){
563                         var selected_date = this.dates.get(-1);
564                         if (selected_date !== undefined) {
565                                 return new Date(selected_date);
566                         } else {
567                                 return null;
568                         }
569                 },
570
571                 clearDates: function(){
572                         this.inputField.val('');
573                         this.update();
574                         this._trigger('changeDate');
575
576                         if (this.o.autoclose) {
577                                 this.hide();
578                         }
579                 },
580
581                 setDates: function(){
582                         var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
583                         this.update.apply(this, args);
584                         this._trigger('changeDate');
585                         this.setValue();
586                         return this;
587                 },
588
589                 setUTCDates: function(){
590                         var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
591                         this.setDates.apply(this, $.map(args, this._utc_to_local));
592                         return this;
593                 },
594
595                 setDate: alias('setDates'),
596                 setUTCDate: alias('setUTCDates'),
597                 remove: alias('destroy', 'Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead'),
598
599                 setValue: function(){
600                         var formatted = this.getFormattedDate();
601                         this.inputField.val(formatted);
602                         return this;
603                 },
604
605                 getFormattedDate: function(format){
606                         if (format === undefined)
607                                 format = this.o.format;
608
609                         var lang = this.o.language;
610                         return $.map(this.dates, function(d){
611                                 return DPGlobal.formatDate(d, format, lang);
612                         }).join(this.o.multidateSeparator);
613                 },
614
615                 getStartDate: function(){
616                         return this.o.startDate;
617                 },
618
619                 setStartDate: function(startDate){
620                         this._process_options({startDate: startDate});
621                         this.update();
622                         this.updateNavArrows();
623                         return this;
624                 },
625
626                 getEndDate: function(){
627                         return this.o.endDate;
628                 },
629
630                 setEndDate: function(endDate){
631                         this._process_options({endDate: endDate});
632                         this.update();
633                         this.updateNavArrows();
634                         return this;
635                 },
636
637                 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
638                         this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
639                         this.update();
640                         return this;
641                 },
642
643                 setDaysOfWeekHighlighted: function(daysOfWeekHighlighted){
644                         this._process_options({daysOfWeekHighlighted: daysOfWeekHighlighted});
645                         this.update();
646                         return this;
647                 },
648
649                 setDatesDisabled: function(datesDisabled){
650                         this._process_options({datesDisabled: datesDisabled});
651                         this.update();
652                         return this;
653                 },
654
655                 place: function(){
656                         if (this.isInline)
657                                 return this;
658                         var calendarWidth = this.picker.outerWidth(),
659                                 calendarHeight = this.picker.outerHeight(),
660                                 visualPadding = 10,
661                                 container = $(this.o.container),
662                                 windowWidth = container.width(),
663                                 scrollTop = this.o.container === 'body' ? $(document).scrollTop() : container.scrollTop(),
664                                 appendOffset = container.offset();
665
666                         var parentsZindex = [0];
667                         this.element.parents().each(function(){
668                                 var itemZIndex = $(this).css('z-index');
669                                 if (itemZIndex !== 'auto' && Number(itemZIndex) !== 0) parentsZindex.push(Number(itemZIndex));
670                         });
671                         var zIndex = Math.max.apply(Math, parentsZindex) + this.o.zIndexOffset;
672                         var offset = this.component ? this.component.parent().offset() : this.element.offset();
673                         var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
674                         var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
675                         var left = offset.left - appendOffset.left;
676                         var top = offset.top - appendOffset.top;
677
678                         if (this.o.container !== 'body') {
679                                 top += scrollTop;
680                         }
681
682                         this.picker.removeClass(
683                                 'datepicker-orient-top datepicker-orient-bottom '+
684                                 'datepicker-orient-right datepicker-orient-left'
685                         );
686
687                         if (this.o.orientation.x !== 'auto'){
688                                 this.picker.addClass('datepicker-orient-' + this.o.orientation.x);
689                                 if (this.o.orientation.x === 'right')
690                                         left -= calendarWidth - width;
691                         }
692                         // auto x orientation is best-placement: if it crosses a window
693                         // edge, fudge it sideways
694                         else {
695                                 if (offset.left < 0) {
696                                         // component is outside the window on the left side. Move it into visible range
697                                         this.picker.addClass('datepicker-orient-left');
698                                         left -= offset.left - visualPadding;
699                                 } else if (left + calendarWidth > windowWidth) {
700                                         // the calendar passes the widow right edge. Align it to component right side
701                                         this.picker.addClass('datepicker-orient-right');
702                                         left += width - calendarWidth;
703                                 } else {
704                                         if (this.o.rtl) {
705                                                 // Default to right
706                                                 this.picker.addClass('datepicker-orient-right');
707                                         } else {
708                                                 // Default to left
709                                                 this.picker.addClass('datepicker-orient-left');
710                                         }
711                                 }
712                         }
713
714                         // auto y orientation is best-situation: top or bottom, no fudging,
715                         // decision based on which shows more of the calendar
716                         var yorient = this.o.orientation.y,
717                                 top_overflow;
718                         if (yorient === 'auto'){
719                                 top_overflow = -scrollTop + top - calendarHeight;
720                                 yorient = top_overflow < 0 ? 'bottom' : 'top';
721                         }
722
723                         this.picker.addClass('datepicker-orient-' + yorient);
724                         if (yorient === 'top')
725                                 top -= calendarHeight + parseInt(this.picker.css('padding-top'));
726                         else
727                                 top += height;
728
729                         if (this.o.rtl) {
730                                 var right = windowWidth - (left + width);
731                                 this.picker.css({
732                                         top: top,
733                                         right: right,
734                                         zIndex: zIndex
735                                 });
736                         } else {
737                                 this.picker.css({
738                                         top: top,
739                                         left: left,
740                                         zIndex: zIndex
741                                 });
742                         }
743                         return this;
744                 },
745
746                 _allow_update: true,
747                 update: function(){
748                         if (!this._allow_update)
749                                 return this;
750
751                         var oldDates = this.dates.copy(),
752                                 dates = [],
753                                 fromArgs = false;
754                         if (arguments.length){
755                                 $.each(arguments, $.proxy(function(i, date){
756                                         if (date instanceof Date)
757                                                 date = this._local_to_utc(date);
758                                         dates.push(date);
759                                 }, this));
760                                 fromArgs = true;
761                         } else {
762                                 dates = this.isInput
763                                                 ? this.element.val()
764                                                 : this.element.data('date') || this.inputField.val();
765                                 if (dates && this.o.multidate)
766                                         dates = dates.split(this.o.multidateSeparator);
767                                 else
768                                         dates = [dates];
769                                 delete this.element.data().date;
770                         }
771
772                         dates = $.map(dates, $.proxy(function(date){
773                                 return DPGlobal.parseDate(date, this.o.format, this.o.language, this.o.assumeNearbyYear);
774                         }, this));
775                         dates = $.grep(dates, $.proxy(function(date){
776                                 return (
777                                         !this.dateWithinRange(date) ||
778                                         !date
779                                 );
780                         }, this), true);
781                         this.dates.replace(dates);
782
783                         if (this.o.updateViewDate) {
784                                 if (this.dates.length)
785                                         this.viewDate = new Date(this.dates.get(-1));
786                                 else if (this.viewDate < this.o.startDate)
787                                         this.viewDate = new Date(this.o.startDate);
788                                 else if (this.viewDate > this.o.endDate)
789                                         this.viewDate = new Date(this.o.endDate);
790                                 else
791                                         this.viewDate = this.o.defaultViewDate;
792                         }
793
794                         if (fromArgs){
795                                 // setting date by clicking
796                                 this.setValue();
797                                 this.element.change();
798                         }
799                         else if (this.dates.length){
800                                 // setting date by typing
801                                 if (String(oldDates) !== String(this.dates) && fromArgs) {
802                                         this._trigger('changeDate');
803                                         this.element.change();
804                                 }
805                         }
806                         if (!this.dates.length && oldDates.length) {
807                                 this._trigger('clearDate');
808                                 this.element.change();
809                         }
810
811                         this.fill();
812                         return this;
813                 },
814
815                 fillDow: function(){
816       if (this.o.showWeekDays) {
817                         var dowCnt = this.o.weekStart,
818                                 html = '<tr>';
819                         if (this.o.calendarWeeks){
820                                 html += '<th class="cw">&#160;</th>';
821                         }
822                         while (dowCnt < this.o.weekStart + 7){
823                                 html += '<th class="dow';
824         if ($.inArray(dowCnt, this.o.daysOfWeekDisabled) !== -1)
825           html += ' disabled';
826         html += '">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
827                         }
828                         html += '</tr>';
829                         this.picker.find('.datepicker-days thead').append(html);
830       }
831                 },
832
833                 fillMonths: function(){
834       var localDate = this._utc_to_local(this.viewDate);
835                         var html = '';
836                         var focused;
837                         for (var i = 0; i < 12; i++){
838                                 focused = localDate && localDate.getMonth() === i ? ' focused' : '';
839                                 html += '<span class="month' + focused + '">' + dates[this.o.language].monthsShort[i] + '</span>';
840                         }
841                         this.picker.find('.datepicker-months td').html(html);
842                 },
843
844                 setRange: function(range){
845                         if (!range || !range.length)
846                                 delete this.range;
847                         else
848                                 this.range = $.map(range, function(d){
849                                         return d.valueOf();
850                                 });
851                         this.fill();
852                 },
853
854                 getClassNames: function(date){
855                         var cls = [],
856                                 year = this.viewDate.getUTCFullYear(),
857                                 month = this.viewDate.getUTCMonth(),
858                                 today = UTCToday();
859                         if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){
860                                 cls.push('old');
861                         } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){
862                                 cls.push('new');
863                         }
864                         if (this.focusDate && date.valueOf() === this.focusDate.valueOf())
865                                 cls.push('focused');
866                         // Compare internal UTC date with UTC today, not local today
867                         if (this.o.todayHighlight && isUTCEquals(date, today)) {
868                                 cls.push('today');
869                         }
870                         if (this.dates.contains(date) !== -1)
871                                 cls.push('active');
872                         if (!this.dateWithinRange(date)){
873                                 cls.push('disabled');
874                         }
875                         if (this.dateIsDisabled(date)){
876                                 cls.push('disabled', 'disabled-date');
877                         }
878                         if ($.inArray(date.getUTCDay(), this.o.daysOfWeekHighlighted) !== -1){
879                                 cls.push('highlighted');
880                         }
881
882                         if (this.range){
883                                 if (date > this.range[0] && date < this.range[this.range.length-1]){
884                                         cls.push('range');
885                                 }
886                                 if ($.inArray(date.valueOf(), this.range) !== -1){
887                                         cls.push('selected');
888                                 }
889                                 if (date.valueOf() === this.range[0]){
890           cls.push('range-start');
891         }
892         if (date.valueOf() === this.range[this.range.length-1]){
893           cls.push('range-end');
894         }
895                         }
896                         return cls;
897                 },
898
899                 _fill_yearsView: function(selector, cssClass, factor, year, startYear, endYear, beforeFn){
900                         var html = '';
901                         var step = factor / 10;
902                         var view = this.picker.find(selector);
903                         var startVal = Math.floor(year / factor) * factor;
904                         var endVal = startVal + step * 9;
905                         var focusedVal = Math.floor(this.viewDate.getFullYear() / step) * step;
906                         var selected = $.map(this.dates, function(d){
907                                 return Math.floor(d.getUTCFullYear() / step) * step;
908                         });
909
910                         var classes, tooltip, before;
911                         for (var currVal = startVal - step; currVal <= endVal + step; currVal += step) {
912                                 classes = [cssClass];
913                                 tooltip = null;
914
915                                 if (currVal === startVal - step) {
916                                         classes.push('old');
917                                 } else if (currVal === endVal + step) {
918                                         classes.push('new');
919                                 }
920                                 if ($.inArray(currVal, selected) !== -1) {
921                                         classes.push('active');
922                                 }
923                                 if (currVal < startYear || currVal > endYear) {
924                                         classes.push('disabled');
925                                 }
926                                 if (currVal === focusedVal) {
927                                   classes.push('focused');
928         }
929
930                                 if (beforeFn !== $.noop) {
931                                         before = beforeFn(new Date(currVal, 0, 1));
932                                         if (before === undefined) {
933                                                 before = {};
934                                         } else if (typeof before === 'boolean') {
935                                                 before = {enabled: before};
936                                         } else if (typeof before === 'string') {
937                                                 before = {classes: before};
938                                         }
939                                         if (before.enabled === false) {
940                                                 classes.push('disabled');
941                                         }
942                                         if (before.classes) {
943                                                 classes = classes.concat(before.classes.split(/\s+/));
944                                         }
945                                         if (before.tooltip) {
946                                                 tooltip = before.tooltip;
947                                         }
948                                 }
949
950                                 html += '<span class="' + classes.join(' ') + '"' + (tooltip ? ' title="' + tooltip + '"' : '') + '>' + currVal + '</span>';
951                         }
952
953                         view.find('.datepicker-switch').text(startVal + '-' + endVal);
954                         view.find('td').html(html);
955                 },
956
957                 fill: function(){
958                         var d = new Date(this.viewDate),
959                                 year = d.getUTCFullYear(),
960                                 month = d.getUTCMonth(),
961                                 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
962                                 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
963                                 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
964                                 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
965                                 todaytxt = dates[this.o.language].today || dates['en'].today || '',
966                                 cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
967         titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat,
968         todayDate = UTCToday(),
969         titleBtnVisible = (this.o.todayBtn === true || this.o.todayBtn === 'linked') && todayDate >= this.o.startDate && todayDate <= this.o.endDate && !this.weekOfDateIsDisabled(todayDate),
970                                 tooltip,
971                                 before;
972                         if (isNaN(year) || isNaN(month))
973                                 return;
974                         this.picker.find('.datepicker-days .datepicker-switch')
975                                                 .text(DPGlobal.formatDate(d, titleFormat, this.o.language));
976                         this.picker.find('tfoot .today')
977                                                 .text(todaytxt)
978             .css('display', titleBtnVisible ? 'table-cell' : 'none');
979                         this.picker.find('tfoot .clear')
980                                                 .text(cleartxt)
981                                                 .css('display', this.o.clearBtn === true ? 'table-cell' : 'none');
982                         this.picker.find('thead .datepicker-title')
983                                                 .text(this.o.title)
984                                                 .css('display', typeof this.o.title === 'string' && this.o.title !== '' ? 'table-cell' : 'none');
985                         this.updateNavArrows();
986                         this.fillMonths();
987                         var prevMonth = UTCDate(year, month, 0),
988                                 day = prevMonth.getUTCDate();
989                         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
990                         var nextMonth = new Date(prevMonth);
991                         if (prevMonth.getUTCFullYear() < 100){
992         nextMonth.setUTCFullYear(prevMonth.getUTCFullYear());
993       }
994                         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
995                         nextMonth = nextMonth.valueOf();
996                         var html = [];
997                         var weekDay, clsName;
998                         while (prevMonth.valueOf() < nextMonth){
999                                 weekDay = prevMonth.getUTCDay();
1000                                 if (weekDay === this.o.weekStart){
1001                                         html.push('<tr>');
1002                                         if (this.o.calendarWeeks){
1003                                                 // ISO 8601: First week contains first thursday.
1004                                                 // ISO also states week starts on Monday, but we can be more abstract here.
1005                                                 var
1006                                                         // Start of current week: based on weekstart/current date
1007                                                         ws = new Date(+prevMonth + (this.o.weekStart - weekDay - 7) % 7 * 864e5),
1008                                                         // Thursday of this week
1009                                                         th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
1010                                                         // First Thursday of year, year from thursday
1011                                                         yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay()) % 7 * 864e5),
1012                                                         // Calendar week: ms between thursdays, div ms per day, div 7 days
1013                                                         calWeek = (th - yth) / 864e5 / 7 + 1;
1014                                                 html.push('<td class="cw">'+ calWeek +'</td>');
1015                                         }
1016                                 }
1017                                 clsName = this.getClassNames(prevMonth);
1018                                 clsName.push('day');
1019
1020                                 var content = prevMonth.getUTCDate();
1021
1022                                 if (this.o.beforeShowDay !== $.noop){
1023                                         before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
1024                                         if (before === undefined)
1025                                                 before = {};
1026                                         else if (typeof before === 'boolean')
1027                                                 before = {enabled: before};
1028                                         else if (typeof before === 'string')
1029                                                 before = {classes: before};
1030                                         if (before.enabled === false)
1031                                                 clsName.push('disabled');
1032                                         if (before.classes)
1033                                                 clsName = clsName.concat(before.classes.split(/\s+/));
1034                                         if (before.tooltip)
1035                                                 tooltip = before.tooltip;
1036                                         if (before.content)
1037                                                 content = before.content;
1038                                 }
1039
1040                                 //Check if uniqueSort exists (supported by jquery >=1.12 and >=2.2)
1041                                 //Fallback to unique function for older jquery versions
1042                                 if ($.isFunction($.uniqueSort)) {
1043                                         clsName = $.uniqueSort(clsName);
1044                                 } else {
1045                                         clsName = $.unique(clsName);
1046                                 }
1047
1048                                 html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + ' data-date="' + prevMonth.getTime().toString() + '">' + content + '</td>');
1049                                 tooltip = null;
1050                                 if (weekDay === this.o.weekEnd){
1051                                         html.push('</tr>');
1052                                 }
1053                                 prevMonth.setUTCDate(prevMonth.getUTCDate() + 1);
1054                         }
1055                         this.picker.find('.datepicker-days tbody').html(html.join(''));
1056
1057                         var monthsTitle = dates[this.o.language].monthsTitle || dates['en'].monthsTitle || 'Months';
1058                         var months = this.picker.find('.datepicker-months')
1059                                                 .find('.datepicker-switch')
1060                                                         .text(this.o.maxViewMode < 2 ? monthsTitle : year)
1061                                                         .end()
1062                                                 .find('tbody span').removeClass('active');
1063
1064                         $.each(this.dates, function(i, d){
1065                                 if (d.getUTCFullYear() === year)
1066                                         months.eq(d.getUTCMonth()).addClass('active');
1067                         });
1068
1069                         if (year < startYear || year > endYear){
1070                                 months.addClass('disabled');
1071                         }
1072                         if (year === startYear){
1073                                 months.slice(0, startMonth).addClass('disabled');
1074                         }
1075                         if (year === endYear){
1076                                 months.slice(endMonth+1).addClass('disabled');
1077                         }
1078
1079                         if (this.o.beforeShowMonth !== $.noop){
1080                                 var that = this;
1081                                 $.each(months, function(i, month){
1082           var moDate = new Date(year, i, 1);
1083           var before = that.o.beforeShowMonth(moDate);
1084                                         if (before === undefined)
1085                                                 before = {};
1086                                         else if (typeof before === 'boolean')
1087                                                 before = {enabled: before};
1088                                         else if (typeof before === 'string')
1089                                                 before = {classes: before};
1090                                         if (before.enabled === false && !$(month).hasClass('disabled'))
1091                                             $(month).addClass('disabled');
1092                                         if (before.classes)
1093                                             $(month).addClass(before.classes);
1094                                         if (before.tooltip)
1095                                             $(month).prop('title', before.tooltip);
1096                                 });
1097                         }
1098
1099                         // Generating decade/years picker
1100                         this._fill_yearsView(
1101                                 '.datepicker-years',
1102                                 'year',
1103                                 10,
1104                                 year,
1105                                 startYear,
1106                                 endYear,
1107                                 this.o.beforeShowYear
1108                         );
1109
1110                         // Generating century/decades picker
1111                         this._fill_yearsView(
1112                                 '.datepicker-decades',
1113                                 'decade',
1114                                 100,
1115                                 year,
1116                                 startYear,
1117                                 endYear,
1118                                 this.o.beforeShowDecade
1119                         );
1120
1121                         // Generating millennium/centuries picker
1122                         this._fill_yearsView(
1123                                 '.datepicker-centuries',
1124                                 'century',
1125                                 1000,
1126                                 year,
1127                                 startYear,
1128                                 endYear,
1129                                 this.o.beforeShowCentury
1130                         );
1131                 },
1132
1133                 updateNavArrows: function(){
1134                         if (!this._allow_update)
1135                                 return;
1136
1137                         var d = new Date(this.viewDate),
1138                                 year = d.getUTCFullYear(),
1139                                 month = d.getUTCMonth(),
1140                                 startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
1141                                 startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
1142                                 endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
1143                                 endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
1144                                 prevIsDisabled,
1145                                 nextIsDisabled,
1146                                 factor = 1;
1147                         switch (this.viewMode){
1148                                 case 4:
1149                                         factor *= 10;
1150                                         /* falls through */
1151                                 case 3:
1152                                         factor *= 10;
1153                                         /* falls through */
1154                                 case 2:
1155                                         factor *= 10;
1156                                         /* falls through */
1157                                 case 1:
1158                                         prevIsDisabled = Math.floor(year / factor) * factor <= startYear;
1159                                         nextIsDisabled = Math.floor(year / factor) * factor + factor > endYear;
1160                                         break;
1161                                 case 0:
1162                                         prevIsDisabled = year <= startYear && month <= startMonth;
1163                                         nextIsDisabled = year >= endYear && month >= endMonth;
1164                                         break;
1165                         }
1166
1167                         this.picker.find('.prev').toggleClass('disabled', prevIsDisabled);
1168                         this.picker.find('.next').toggleClass('disabled', nextIsDisabled);
1169                 },
1170
1171                 click: function(e){
1172                         e.preventDefault();
1173                         e.stopPropagation();
1174
1175                         var target, dir, day, year, month;
1176                         target = $(e.target);
1177
1178                         // Clicked on the switch
1179                         if (target.hasClass('datepicker-switch') && this.viewMode !== this.o.maxViewMode){
1180                                 this.setViewMode(this.viewMode + 1);
1181                         }
1182
1183                         // Clicked on today button
1184                         if (target.hasClass('today') && !target.hasClass('day')){
1185                                 this.setViewMode(0);
1186                                 this._setDate(UTCToday(), this.o.todayBtn === 'linked' ? null : 'view');
1187                         }
1188
1189                         // Clicked on clear button
1190                         if (target.hasClass('clear')){
1191                                 this.clearDates();
1192                         }
1193
1194                         if (!target.hasClass('disabled')){
1195                                 // Clicked on a month, year, decade, century
1196                                 if (target.hasClass('month')
1197                                                 || target.hasClass('year')
1198                                                 || target.hasClass('decade')
1199                                                 || target.hasClass('century')) {
1200                                         this.viewDate.setUTCDate(1);
1201
1202                                         day = 1;
1203                                         if (this.viewMode === 1){
1204                                                 month = target.parent().find('span').index(target);
1205                                                 year = this.viewDate.getUTCFullYear();
1206                                                 this.viewDate.setUTCMonth(month);
1207                                         } else {
1208                                                 month = 0;
1209                                                 year = Number(target.text());
1210                                                 this.viewDate.setUTCFullYear(year);
1211                                         }
1212
1213                                         this._trigger(DPGlobal.viewModes[this.viewMode - 1].e, this.viewDate);
1214
1215                                         if (this.viewMode === this.o.minViewMode){
1216                                                 this._setDate(UTCDate(year, month, day));
1217                                         } else {
1218                                                 this.setViewMode(this.viewMode - 1);
1219                                                 this.fill();
1220                                         }
1221                                 }
1222                         }
1223
1224                         if (this.picker.is(':visible') && this._focused_from){
1225                                 this._focused_from.focus();
1226                         }
1227                         delete this._focused_from;
1228                 },
1229
1230                 dayCellClick: function(e){
1231                         var $target = $(e.currentTarget);
1232                         var timestamp = $target.data('date');
1233                         var date = new Date(timestamp);
1234
1235                         if (this.o.updateViewDate) {
1236                                 if (date.getUTCFullYear() !== this.viewDate.getUTCFullYear()) {
1237                                         this._trigger('changeYear', this.viewDate);
1238                                 }
1239
1240                                 if (date.getUTCMonth() !== this.viewDate.getUTCMonth()) {
1241                                         this._trigger('changeMonth', this.viewDate);
1242                                 }
1243                         }
1244                         this._setDate(date);
1245                 },
1246
1247                 // Clicked on prev or next
1248                 navArrowsClick: function(e){
1249                         var $target = $(e.currentTarget);
1250                         var dir = $target.hasClass('prev') ? -1 : 1;
1251                         if (this.viewMode !== 0){
1252                                 dir *= DPGlobal.viewModes[this.viewMode].navStep * 12;
1253                         }
1254                         this.viewDate = this.moveMonth(this.viewDate, dir);
1255                         this._trigger(DPGlobal.viewModes[this.viewMode].e, this.viewDate);
1256                         this.fill();
1257                 },
1258
1259                 _toggle_multidate: function(date){
1260                         var ix = this.dates.contains(date);
1261                         if (!date){
1262                                 this.dates.clear();
1263                         }
1264
1265                         if (ix !== -1){
1266                                 if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){
1267                                         this.dates.remove(ix);
1268                                 }
1269                         } else if (this.o.multidate === false) {
1270                                 this.dates.clear();
1271                                 this.dates.push(date);
1272                         }
1273                         else {
1274                                 this.dates.push(date);
1275                         }
1276
1277                         if (typeof this.o.multidate === 'number')
1278                                 while (this.dates.length > this.o.multidate)
1279                                         this.dates.remove(0);
1280                 },
1281
1282                 _setDate: function(date, which){
1283                         if (!which || which === 'date')
1284                                 this._toggle_multidate(date && new Date(date));
1285                         if ((!which && this.o.updateViewDate) || which === 'view')
1286                                 this.viewDate = date && new Date(date);
1287
1288                         this.fill();
1289                         this.setValue();
1290                         if (!which || which !== 'view') {
1291                                 this._trigger('changeDate');
1292                         }
1293                         this.inputField.trigger('change');
1294                         if (this.o.autoclose && (!which || which === 'date')){
1295                                 this.hide();
1296                         }
1297                 },
1298
1299                 moveDay: function(date, dir){
1300                         var newDate = new Date(date);
1301                         newDate.setUTCDate(date.getUTCDate() + dir);
1302
1303                         return newDate;
1304                 },
1305
1306                 moveWeek: function(date, dir){
1307                         return this.moveDay(date, dir * 7);
1308                 },
1309
1310                 moveMonth: function(date, dir){
1311                         if (!isValidDate(date))
1312                                 return this.o.defaultViewDate;
1313                         if (!dir)
1314                                 return date;
1315                         var new_date = new Date(date.valueOf()),
1316                                 day = new_date.getUTCDate(),
1317                                 month = new_date.getUTCMonth(),
1318                                 mag = Math.abs(dir),
1319                                 new_month, test;
1320                         dir = dir > 0 ? 1 : -1;
1321                         if (mag === 1){
1322                                 test = dir === -1
1323                                         // If going back one month, make sure month is not current month
1324                                         // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
1325                                         ? function(){
1326                                                 return new_date.getUTCMonth() === month;
1327                                         }
1328                                         // If going forward one month, make sure month is as expected
1329                                         // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
1330                                         : function(){
1331                                                 return new_date.getUTCMonth() !== new_month;
1332                                         };
1333                                 new_month = month + dir;
1334                                 new_date.setUTCMonth(new_month);
1335                                 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
1336                                 new_month = (new_month + 12) % 12;
1337                         }
1338                         else {
1339                                 // For magnitudes >1, move one month at a time...
1340                                 for (var i=0; i < mag; i++)
1341                                         // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
1342                                         new_date = this.moveMonth(new_date, dir);
1343                                 // ...then reset the day, keeping it in the new month
1344                                 new_month = new_date.getUTCMonth();
1345                                 new_date.setUTCDate(day);
1346                                 test = function(){
1347                                         return new_month !== new_date.getUTCMonth();
1348                                 };
1349                         }
1350                         // Common date-resetting loop -- if date is beyond end of month, make it
1351                         // end of month
1352                         while (test()){
1353                                 new_date.setUTCDate(--day);
1354                                 new_date.setUTCMonth(new_month);
1355                         }
1356                         return new_date;
1357                 },
1358
1359                 moveYear: function(date, dir){
1360                         return this.moveMonth(date, dir*12);
1361                 },
1362
1363                 moveAvailableDate: function(date, dir, fn){
1364                         do {
1365                                 date = this[fn](date, dir);
1366
1367                                 if (!this.dateWithinRange(date))
1368                                         return false;
1369
1370                                 fn = 'moveDay';
1371                         }
1372                         while (this.dateIsDisabled(date));
1373
1374                         return date;
1375                 },
1376
1377                 weekOfDateIsDisabled: function(date){
1378                         return $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1;
1379                 },
1380
1381                 dateIsDisabled: function(date){
1382                         return (
1383                                 this.weekOfDateIsDisabled(date) ||
1384                                 $.grep(this.o.datesDisabled, function(d){
1385                                         return isUTCEquals(date, d);
1386                                 }).length > 0
1387                         );
1388                 },
1389
1390                 dateWithinRange: function(date){
1391                         return date >= this.o.startDate && date <= this.o.endDate;
1392                 },
1393
1394                 keydown: function(e){
1395                         if (!this.picker.is(':visible')){
1396                                 if (e.keyCode === 40 || e.keyCode === 27) { // allow down to re-show picker
1397                                         this.show();
1398                                         e.stopPropagation();
1399         }
1400                                 return;
1401                         }
1402                         var dateChanged = false,
1403                                 dir, newViewDate,
1404                                 focusDate = this.focusDate || this.viewDate;
1405                         switch (e.keyCode){
1406                                 case 27: // escape
1407                                         if (this.focusDate){
1408                                                 this.focusDate = null;
1409                                                 this.viewDate = this.dates.get(-1) || this.viewDate;
1410                                                 this.fill();
1411                                         }
1412                                         else
1413                                                 this.hide();
1414                                         e.preventDefault();
1415                                         e.stopPropagation();
1416                                         break;
1417                                 case 37: // left
1418                                 case 38: // up
1419                                 case 39: // right
1420                                 case 40: // down
1421                                         if (!this.o.keyboardNavigation || this.o.daysOfWeekDisabled.length === 7)
1422                                                 break;
1423                                         dir = e.keyCode === 37 || e.keyCode === 38 ? -1 : 1;
1424           if (this.viewMode === 0) {
1425                                         if (e.ctrlKey){
1426                                                 newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear');
1427
1428                                                 if (newViewDate)
1429                                                         this._trigger('changeYear', this.viewDate);
1430                                         } else if (e.shiftKey){
1431                                                 newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth');
1432
1433                                                 if (newViewDate)
1434                                                         this._trigger('changeMonth', this.viewDate);
1435                                         } else if (e.keyCode === 37 || e.keyCode === 39){
1436                                                 newViewDate = this.moveAvailableDate(focusDate, dir, 'moveDay');
1437                                         } else if (!this.weekOfDateIsDisabled(focusDate)){
1438                                                 newViewDate = this.moveAvailableDate(focusDate, dir, 'moveWeek');
1439                                         }
1440           } else if (this.viewMode === 1) {
1441             if (e.keyCode === 38 || e.keyCode === 40) {
1442               dir = dir * 4;
1443             }
1444             newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth');
1445           } else if (this.viewMode === 2) {
1446             if (e.keyCode === 38 || e.keyCode === 40) {
1447               dir = dir * 4;
1448             }
1449             newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear');
1450           }
1451                                         if (newViewDate){
1452                                                 this.focusDate = this.viewDate = newViewDate;
1453                                                 this.setValue();
1454                                                 this.fill();
1455                                                 e.preventDefault();
1456                                         }
1457                                         break;
1458                                 case 13: // enter
1459                                         if (!this.o.forceParse)
1460                                                 break;
1461                                         focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
1462                                         if (this.o.keyboardNavigation) {
1463                                                 this._toggle_multidate(focusDate);
1464                                                 dateChanged = true;
1465                                         }
1466                                         this.focusDate = null;
1467                                         this.viewDate = this.dates.get(-1) || this.viewDate;
1468                                         this.setValue();
1469                                         this.fill();
1470                                         if (this.picker.is(':visible')){
1471                                                 e.preventDefault();
1472                                                 e.stopPropagation();
1473                                                 if (this.o.autoclose)
1474                                                         this.hide();
1475                                         }
1476                                         break;
1477                                 case 9: // tab
1478                                         this.focusDate = null;
1479                                         this.viewDate = this.dates.get(-1) || this.viewDate;
1480                                         this.fill();
1481                                         this.hide();
1482                                         break;
1483                         }
1484                         if (dateChanged){
1485                                 if (this.dates.length)
1486                                         this._trigger('changeDate');
1487                                 else
1488                                         this._trigger('clearDate');
1489                                 this.inputField.trigger('change');
1490                         }
1491                 },
1492
1493                 setViewMode: function(viewMode){
1494                         this.viewMode = viewMode;
1495                         this.picker
1496                                 .children('div')
1497                                 .hide()
1498                                 .filter('.datepicker-' + DPGlobal.viewModes[this.viewMode].clsName)
1499                                         .show();
1500                         this.updateNavArrows();
1501       this._trigger('changeViewMode', new Date(this.viewDate));
1502                 }
1503         };
1504
1505         var DateRangePicker = function(element, options){
1506                 $.data(element, 'datepicker', this);
1507                 this.element = $(element);
1508                 this.inputs = $.map(options.inputs, function(i){
1509                         return i.jquery ? i[0] : i;
1510                 });
1511                 delete options.inputs;
1512
1513                 this.keepEmptyValues = options.keepEmptyValues;
1514                 delete options.keepEmptyValues;
1515
1516                 datepickerPlugin.call($(this.inputs), options)
1517                         .on('changeDate', $.proxy(this.dateUpdated, this));
1518
1519                 this.pickers = $.map(this.inputs, function(i){
1520                         return $.data(i, 'datepicker');
1521                 });
1522                 this.updateDates();
1523         };
1524         DateRangePicker.prototype = {
1525                 updateDates: function(){
1526                         this.dates = $.map(this.pickers, function(i){
1527                                 return i.getUTCDate();
1528                         });
1529                         this.updateRanges();
1530                 },
1531                 updateRanges: function(){
1532                         var range = $.map(this.dates, function(d){
1533                                 return d.valueOf();
1534                         });
1535                         $.each(this.pickers, function(i, p){
1536                                 p.setRange(range);
1537                         });
1538                 },
1539                 clearDates: function(){
1540                         $.each(this.pickers, function(i, p){
1541                                 p.clearDates();
1542                         });
1543                 },
1544                 dateUpdated: function(e){
1545                         // `this.updating` is a workaround for preventing infinite recursion
1546                         // between `changeDate` triggering and `setUTCDate` calling.  Until
1547                         // there is a better mechanism.
1548                         if (this.updating)
1549                                 return;
1550                         this.updating = true;
1551
1552                         var dp = $.data(e.target, 'datepicker');
1553
1554                         if (dp === undefined) {
1555                                 return;
1556                         }
1557
1558                         var new_date = dp.getUTCDate(),
1559                                 keep_empty_values = this.keepEmptyValues,
1560                                 i = $.inArray(e.target, this.inputs),
1561                                 j = i - 1,
1562                                 k = i + 1,
1563                                 l = this.inputs.length;
1564                         if (i === -1)
1565                                 return;
1566
1567                         $.each(this.pickers, function(i, p){
1568                                 if (!p.getUTCDate() && (p === dp || !keep_empty_values))
1569                                         p.setUTCDate(new_date);
1570                         });
1571
1572                         if (new_date < this.dates[j]){
1573                                 // Date being moved earlier/left
1574                                 while (j >= 0 && new_date < this.dates[j]){
1575                                         this.pickers[j--].setUTCDate(new_date);
1576                                 }
1577                         } else if (new_date > this.dates[k]){
1578                                 // Date being moved later/right
1579                                 while (k < l && new_date > this.dates[k]){
1580                                         this.pickers[k++].setUTCDate(new_date);
1581                                 }
1582                         }
1583                         this.updateDates();
1584
1585                         delete this.updating;
1586                 },
1587                 destroy: function(){
1588                         $.map(this.pickers, function(p){ p.destroy(); });
1589                         $(this.inputs).off('changeDate', this.dateUpdated);
1590                         delete this.element.data().datepicker;
1591                 },
1592                 remove: alias('destroy', 'Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead')
1593         };
1594
1595         function opts_from_el(el, prefix){
1596                 // Derive options from element data-attrs
1597                 var data = $(el).data(),
1598                         out = {}, inkey,
1599                         replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])');
1600                 prefix = new RegExp('^' + prefix.toLowerCase());
1601                 function re_lower(_,a){
1602                         return a.toLowerCase();
1603                 }
1604                 for (var key in data)
1605                         if (prefix.test(key)){
1606                                 inkey = key.replace(replace, re_lower);
1607                                 out[inkey] = data[key];
1608                         }
1609                 return out;
1610         }
1611
1612         function opts_from_locale(lang){
1613                 // Derive options from locale plugins
1614                 var out = {};
1615                 // Check if "de-DE" style date is available, if not language should
1616                 // fallback to 2 letter code eg "de"
1617                 if (!dates[lang]){
1618                         lang = lang.split('-')[0];
1619                         if (!dates[lang])
1620                                 return;
1621                 }
1622                 var d = dates[lang];
1623                 $.each(locale_opts, function(i,k){
1624                         if (k in d)
1625                                 out[k] = d[k];
1626                 });
1627                 return out;
1628         }
1629
1630         var old = $.fn.datepicker;
1631         var datepickerPlugin = function(option){
1632                 var args = Array.apply(null, arguments);
1633                 args.shift();
1634                 var internal_return;
1635                 this.each(function(){
1636                         var $this = $(this),
1637                                 data = $this.data('datepicker'),
1638                                 options = typeof option === 'object' && option;
1639                         if (!data){
1640                                 var elopts = opts_from_el(this, 'date'),
1641                                         // Preliminary otions
1642                                         xopts = $.extend({}, defaults, elopts, options),
1643                                         locopts = opts_from_locale(xopts.language),
1644                                         // Options priority: js args, data-attrs, locales, defaults
1645                                         opts = $.extend({}, defaults, locopts, elopts, options);
1646                                 if ($this.hasClass('input-daterange') || opts.inputs){
1647                                         $.extend(opts, {
1648                                                 inputs: opts.inputs || $this.find('input').toArray()
1649                                         });
1650                                         data = new DateRangePicker(this, opts);
1651                                 }
1652                                 else {
1653                                         data = new Datepicker(this, opts);
1654                                 }
1655                                 $this.data('datepicker', data);
1656                         }
1657                         if (typeof option === 'string' && typeof data[option] === 'function'){
1658                                 internal_return = data[option].apply(data, args);
1659                         }
1660                 });
1661
1662                 if (
1663                         internal_return === undefined ||
1664                         internal_return instanceof Datepicker ||
1665                         internal_return instanceof DateRangePicker
1666                 )
1667                         return this;
1668
1669                 if (this.length > 1)
1670                         throw new Error('Using only allowed for the collection of a single element (' + option + ' function)');
1671                 else
1672                         return internal_return;
1673         };
1674         $.fn.datepicker = datepickerPlugin;
1675
1676         var defaults = $.fn.datepicker.defaults = {
1677                 assumeNearbyYear: false,
1678                 autoclose: false,
1679                 beforeShowDay: $.noop,
1680                 beforeShowMonth: $.noop,
1681                 beforeShowYear: $.noop,
1682                 beforeShowDecade: $.noop,
1683                 beforeShowCentury: $.noop,
1684                 calendarWeeks: false,
1685                 clearBtn: false,
1686                 toggleActive: false,
1687                 daysOfWeekDisabled: [],
1688                 daysOfWeekHighlighted: [],
1689                 datesDisabled: [],
1690                 endDate: Infinity,
1691                 forceParse: true,
1692                 format: 'mm/dd/yyyy',
1693                 keepEmptyValues: false,
1694                 keyboardNavigation: true,
1695                 language: 'en',
1696                 minViewMode: 0,
1697                 maxViewMode: 4,
1698                 multidate: false,
1699                 multidateSeparator: ',',
1700                 orientation: "auto",
1701                 rtl: false,
1702                 startDate: -Infinity,
1703                 startView: 0,
1704                 todayBtn: false,
1705                 todayHighlight: false,
1706                 updateViewDate: true,
1707                 weekStart: 0,
1708                 disableTouchKeyboard: false,
1709                 enableOnReadonly: true,
1710                 showOnFocus: true,
1711                 zIndexOffset: 10,
1712                 container: 'body',
1713                 immediateUpdates: false,
1714                 title: '',
1715                 templates: {
1716                         leftArrow: '&#x00AB;',
1717                         rightArrow: '&#x00BB;'
1718                 },
1719     showWeekDays: true
1720         };
1721         var locale_opts = $.fn.datepicker.locale_opts = [
1722                 'format',
1723                 'rtl',
1724                 'weekStart'
1725         ];
1726         $.fn.datepicker.Constructor = Datepicker;
1727         var dates = $.fn.datepicker.dates = {
1728                 en: {
1729                         days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
1730                         daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
1731                         daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
1732                         months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1733                         monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1734                         today: "Today",
1735                         clear: "Clear",
1736                         titleFormat: "MM yyyy"
1737                 }
1738         };
1739
1740         var DPGlobal = {
1741                 viewModes: [
1742                         {
1743                                 names: ['days', 'month'],
1744                                 clsName: 'days',
1745                                 e: 'changeMonth'
1746                         },
1747                         {
1748                                 names: ['months', 'year'],
1749                                 clsName: 'months',
1750                                 e: 'changeYear',
1751                                 navStep: 1
1752                         },
1753                         {
1754                                 names: ['years', 'decade'],
1755                                 clsName: 'years',
1756                                 e: 'changeDecade',
1757                                 navStep: 10
1758                         },
1759                         {
1760                                 names: ['decades', 'century'],
1761                                 clsName: 'decades',
1762                                 e: 'changeCentury',
1763                                 navStep: 100
1764                         },
1765                         {
1766                                 names: ['centuries', 'millennium'],
1767                                 clsName: 'centuries',
1768                                 e: 'changeMillennium',
1769                                 navStep: 1000
1770                         }
1771                 ],
1772                 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
1773                 nonpunctuation: /[^ -\/:-@\u5e74\u6708\u65e5\[-`{-~\t\n\r]+/g,
1774                 parseFormat: function(format){
1775                         if (typeof format.toValue === 'function' && typeof format.toDisplay === 'function')
1776                 return format;
1777             // IE treats \0 as a string end in inputs (truncating the value),
1778                         // so it's a bad format delimiter, anyway
1779                         var separators = format.replace(this.validParts, '\0').split('\0'),
1780                                 parts = format.match(this.validParts);
1781                         if (!separators || !separators.length || !parts || parts.length === 0){
1782                                 throw new Error("Invalid date format.");
1783                         }
1784                         return {separators: separators, parts: parts};
1785                 },
1786                 parseDate: function(date, format, language, assumeNearby){
1787                         if (!date)
1788                                 return undefined;
1789                         if (date instanceof Date)
1790                                 return date;
1791                         if (typeof format === 'string')
1792                                 format = DPGlobal.parseFormat(format);
1793                         if (format.toValue)
1794                                 return format.toValue(date, format, language);
1795                         var fn_map = {
1796                                         d: 'moveDay',
1797                                         m: 'moveMonth',
1798                                         w: 'moveWeek',
1799                                         y: 'moveYear'
1800                                 },
1801                                 dateAliases = {
1802                                         yesterday: '-1d',
1803                                         today: '+0d',
1804                                         tomorrow: '+1d'
1805                                 },
1806                                 parts, part, dir, i, fn;
1807                         if (date in dateAliases){
1808                                 date = dateAliases[date];
1809                         }
1810                         if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/i.test(date)){
1811                                 parts = date.match(/([\-+]\d+)([dmwy])/gi);
1812                                 date = new Date();
1813                                 for (i=0; i < parts.length; i++){
1814                                         part = parts[i].match(/([\-+]\d+)([dmwy])/i);
1815                                         dir = Number(part[1]);
1816                                         fn = fn_map[part[2].toLowerCase()];
1817                                         date = Datepicker.prototype[fn](date, dir);
1818                                 }
1819                                 return Datepicker.prototype._zero_utc_time(date);
1820                         }
1821
1822                         parts = date && date.match(this.nonpunctuation) || [];
1823
1824                         function applyNearbyYear(year, threshold){
1825                                 if (threshold === true)
1826                                         threshold = 10;
1827
1828                                 // if year is 2 digits or less, than the user most likely is trying to get a recent century
1829                                 if (year < 100){
1830                                         year += 2000;
1831                                         // if the new year is more than threshold years in advance, use last century
1832                                         if (year > ((new Date()).getFullYear()+threshold)){
1833                                                 year -= 100;
1834                                         }
1835                                 }
1836
1837                                 return year;
1838                         }
1839
1840                         var parsed = {},
1841                                 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
1842                                 setters_map = {
1843                                         yyyy: function(d,v){
1844                                                 return d.setUTCFullYear(assumeNearby ? applyNearbyYear(v, assumeNearby) : v);
1845                                         },
1846                                         m: function(d,v){
1847                                                 if (isNaN(d))
1848                                                         return d;
1849                                                 v -= 1;
1850                                                 while (v < 0) v += 12;
1851                                                 v %= 12;
1852                                                 d.setUTCMonth(v);
1853                                                 while (d.getUTCMonth() !== v)
1854                                                         d.setUTCDate(d.getUTCDate()-1);
1855                                                 return d;
1856                                         },
1857                                         d: function(d,v){
1858                                                 return d.setUTCDate(v);
1859                                         }
1860                                 },
1861                                 val, filtered;
1862                         setters_map['yy'] = setters_map['yyyy'];
1863                         setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1864                         setters_map['dd'] = setters_map['d'];
1865                         date = UTCToday();
1866                         var fparts = format.parts.slice();
1867                         // Remove noop parts
1868                         if (parts.length !== fparts.length){
1869                                 fparts = $(fparts).filter(function(i,p){
1870                                         return $.inArray(p, setters_order) !== -1;
1871                                 }).toArray();
1872                         }
1873                         // Process remainder
1874                         function match_part(){
1875                                 var m = this.slice(0, parts[i].length),
1876                                         p = parts[i].slice(0, m.length);
1877                                 return m.toLowerCase() === p.toLowerCase();
1878                         }
1879                         if (parts.length === fparts.length){
1880                                 var cnt;
1881                                 for (i=0, cnt = fparts.length; i < cnt; i++){
1882                                         val = parseInt(parts[i], 10);
1883                                         part = fparts[i];
1884                                         if (isNaN(val)){
1885                                                 switch (part){
1886                                                         case 'MM':
1887                                                                 filtered = $(dates[language].months).filter(match_part);
1888                                                                 val = $.inArray(filtered[0], dates[language].months) + 1;
1889                                                                 break;
1890                                                         case 'M':
1891                                                                 filtered = $(dates[language].monthsShort).filter(match_part);
1892                                                                 val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1893                                                                 break;
1894                                                 }
1895                                         }
1896                                         parsed[part] = val;
1897                                 }
1898                                 var _date, s;
1899                                 for (i=0; i < setters_order.length; i++){
1900                                         s = setters_order[i];
1901                                         if (s in parsed && !isNaN(parsed[s])){
1902                                                 _date = new Date(date);
1903                                                 setters_map[s](_date, parsed[s]);
1904                                                 if (!isNaN(_date))
1905                                                         date = _date;
1906                                         }
1907                                 }
1908                         }
1909                         return date;
1910                 },
1911                 formatDate: function(date, format, language){
1912                         if (!date)
1913                                 return '';
1914                         if (typeof format === 'string')
1915                                 format = DPGlobal.parseFormat(format);
1916                         if (format.toDisplay)
1917                 return format.toDisplay(date, format, language);
1918             var val = {
1919                                 d: date.getUTCDate(),
1920                                 D: dates[language].daysShort[date.getUTCDay()],
1921                                 DD: dates[language].days[date.getUTCDay()],
1922                                 m: date.getUTCMonth() + 1,
1923                                 M: dates[language].monthsShort[date.getUTCMonth()],
1924                                 MM: dates[language].months[date.getUTCMonth()],
1925                                 yy: date.getUTCFullYear().toString().substring(2),
1926                                 yyyy: date.getUTCFullYear()
1927                         };
1928                         val.dd = (val.d < 10 ? '0' : '') + val.d;
1929                         val.mm = (val.m < 10 ? '0' : '') + val.m;
1930                         date = [];
1931                         var seps = $.extend([], format.separators);
1932                         for (var i=0, cnt = format.parts.length; i <= cnt; i++){
1933                                 if (seps.length)
1934                                         date.push(seps.shift());
1935                                 date.push(val[format.parts[i]]);
1936                         }
1937                         return date.join('');
1938                 },
1939                 headTemplate: '<thead>'+
1940                                       '<tr>'+
1941                                         '<th colspan="7" class="datepicker-title"></th>'+
1942                                       '</tr>'+
1943                                                         '<tr>'+
1944                                                                 '<th class="prev">'+defaults.templates.leftArrow+'</th>'+
1945                                                                 '<th colspan="5" class="datepicker-switch"></th>'+
1946                                                                 '<th class="next">'+defaults.templates.rightArrow+'</th>'+
1947                                                         '</tr>'+
1948                                                 '</thead>',
1949                 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
1950                 footTemplate: '<tfoot>'+
1951                                                         '<tr>'+
1952                                                                 '<th colspan="7" class="today"></th>'+
1953                                                         '</tr>'+
1954                                                         '<tr>'+
1955                                                                 '<th colspan="7" class="clear"></th>'+
1956                                                         '</tr>'+
1957                                                 '</tfoot>'
1958         };
1959         DPGlobal.template = '<div class="datepicker">'+
1960                                                         '<div class="datepicker-days">'+
1961                                                                 '<table class="table-condensed">'+
1962                                                                         DPGlobal.headTemplate+
1963                                                                         '<tbody></tbody>'+
1964                                                                         DPGlobal.footTemplate+
1965                                                                 '</table>'+
1966                                                         '</div>'+
1967                                                         '<div class="datepicker-months">'+
1968                                                                 '<table class="table-condensed">'+
1969                                                                         DPGlobal.headTemplate+
1970                                                                         DPGlobal.contTemplate+
1971                                                                         DPGlobal.footTemplate+
1972                                                                 '</table>'+
1973                                                         '</div>'+
1974                                                         '<div class="datepicker-years">'+
1975                                                                 '<table class="table-condensed">'+
1976                                                                         DPGlobal.headTemplate+
1977                                                                         DPGlobal.contTemplate+
1978                                                                         DPGlobal.footTemplate+
1979                                                                 '</table>'+
1980                                                         '</div>'+
1981                                                         '<div class="datepicker-decades">'+
1982                                                                 '<table class="table-condensed">'+
1983                                                                         DPGlobal.headTemplate+
1984                                                                         DPGlobal.contTemplate+
1985                                                                         DPGlobal.footTemplate+
1986                                                                 '</table>'+
1987                                                         '</div>'+
1988                                                         '<div class="datepicker-centuries">'+
1989                                                                 '<table class="table-condensed">'+
1990                                                                         DPGlobal.headTemplate+
1991                                                                         DPGlobal.contTemplate+
1992                                                                         DPGlobal.footTemplate+
1993                                                                 '</table>'+
1994                                                         '</div>'+
1995                                                 '</div>';
1996
1997         $.fn.datepicker.DPGlobal = DPGlobal;
1998
1999
2000         /* DATEPICKER NO CONFLICT
2001         * =================== */
2002
2003         $.fn.datepicker.noConflict = function(){
2004                 $.fn.datepicker = old;
2005                 return this;
2006         };
2007
2008         /* DATEPICKER VERSION
2009          * =================== */
2010         $.fn.datepicker.version = '1.9.0';
2011
2012         $.fn.datepicker.deprecated = function(msg){
2013                 var console = window.console;
2014                 if (console && console.warn) {
2015                         console.warn('DEPRECATED: ' + msg);
2016                 }
2017         };
2018
2019
2020         /* DATEPICKER DATA-API
2021         * ================== */
2022
2023         $(document).on(
2024                 'focus.datepicker.data-api click.datepicker.data-api',
2025                 '[data-provide="datepicker"]',
2026                 function(e){
2027                         var $this = $(this);
2028                         if ($this.data('datepicker'))
2029                                 return;
2030                         e.preventDefault();
2031                         // component click requires us to explicitly show it
2032                         datepickerPlugin.call($this, 'show');
2033                 }
2034         );
2035         $(function(){
2036                 datepickerPlugin.call($('[data-provide="datepicker-inline"]'));
2037         });
2038
2039 }));