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