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