flashgrid 1.3.0 → 2.0.0

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