bootstrap-rails-engine 1.1.0 → 1.2.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.
@@ -20,9 +20,19 @@
20
20
 
21
21
  !function( $ ) {
22
22
 
23
+ function UTCDate(){
24
+ return new Date(Date.UTC.apply(Date, arguments));
25
+ }
26
+ function UTCToday(){
27
+ var today = new Date();
28
+ return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
29
+ }
30
+
23
31
  // Picker object
24
32
 
25
- var Datepicker = function(element, options){
33
+ var Datepicker = function(element, options) {
34
+ var that = this;
35
+
26
36
  this.element = $(element);
27
37
  this.language = options.language||this.element.data('date-language')||"en";
28
38
  this.language = this.language in dates ? this.language : "en";
@@ -30,41 +40,42 @@
30
40
  this.picker = $(DPGlobal.template)
31
41
  .appendTo('body')
32
42
  .on({
33
- click: $.proxy(this.click, this),
34
- mousedown: $.proxy(this.mousedown, this)
43
+ click: $.proxy(this.click, this)
35
44
  });
36
45
  this.isInput = this.element.is('input');
37
46
  this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
47
+ this.hasInput = this.component && this.element.find('input').length;
38
48
  if(this.component && this.component.length === 0)
39
49
  this.component = false;
40
50
 
41
51
  if (this.isInput) {
42
52
  this.element.on({
43
53
  focus: $.proxy(this.show, this),
44
- blur: $.proxy(this._hide, this),
45
54
  keyup: $.proxy(this.update, this),
46
55
  keydown: $.proxy(this.keydown, this)
47
56
  });
48
57
  } else {
49
- if (this.component){
58
+ if (this.component && this.hasInput){
50
59
  // For components that are not readonly, allow keyboard nav
51
60
  this.element.find('input').on({
52
61
  focus: $.proxy(this.show, this),
53
- blur: $.proxy(this._hide, this),
54
62
  keyup: $.proxy(this.update, this),
55
63
  keydown: $.proxy(this.keydown, this)
56
64
  });
57
65
 
58
66
  this.component.on('click', $.proxy(this.show, this));
59
- var element = this.element.find('input');
60
- element.on({
61
- blur: $.proxy(this._hide, this)
62
- })
63
67
  } else {
64
68
  this.element.on('click', $.proxy(this.show, this));
65
69
  }
66
70
  }
67
71
 
72
+ $(document).on('mousedown', function (e) {
73
+ // Clicked outside the datepicker, hide it
74
+ if ($(e.target).closest('.datepicker').length == 0) {
75
+ that.hide();
76
+ }
77
+ });
78
+
68
79
  this.autoclose = false;
69
80
  if ('autoclose' in options) {
70
81
  this.autoclose = options.autoclose;
@@ -72,7 +83,14 @@
72
83
  this.autoclose = this.element.data('date-autoclose');
73
84
  }
74
85
 
75
- switch(options.startView){
86
+ this.keyboardNavigation = true;
87
+ if ('keyboardNavigation' in options) {
88
+ this.keyboardNavigation = options.keyboardNavigation;
89
+ } else if ('dateKeyboardNavigation' in this.element.data()) {
90
+ this.keyboardNavigation = this.element.data('date-keyboard-navigation');
91
+ }
92
+
93
+ switch(options.startView || this.element.data('date-start-view')){
76
94
  case 2:
77
95
  case 'decade':
78
96
  this.viewMode = this.startViewMode = 2;
@@ -88,6 +106,9 @@
88
106
  break;
89
107
  }
90
108
 
109
+ this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
110
+ this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
111
+
91
112
  this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
92
113
  this.weekEnd = ((this.weekStart + 6) % 7);
93
114
  this.startDate = -Infinity;
@@ -106,46 +127,19 @@
106
127
  show: function(e) {
107
128
  this.picker.show();
108
129
  this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
130
+ this.update();
109
131
  this.place();
110
132
  $(window).on('resize', $.proxy(this.place, this));
111
133
  if (e ) {
112
134
  e.stopPropagation();
113
135
  e.preventDefault();
114
136
  }
115
- if (!this.isInput) {
116
- $(document).on('mousedown', $.proxy(this.hide, this));
117
- }
118
137
  this.element.trigger({
119
138
  type: 'show',
120
139
  date: this.date
121
140
  });
122
141
  },
123
142
 
124
- _hide: function(e){
125
- // When going from the input to the picker, IE handles the blur/click
126
- // events differently than other browsers, in such a way that the blur
127
- // event triggers a hide before the click event can stop propagation.
128
- if ($.browser.msie) {
129
- var t = this, args = arguments;
130
-
131
- function cancel_hide(){
132
- clearTimeout(hide_timeout);
133
- e.target.focus();
134
- t.picker.off('click', cancel_hide);
135
- }
136
-
137
- function do_hide(){
138
- t.hide.apply(t, args);
139
- t.picker.off('click', cancel_hide);
140
- }
141
-
142
- this.picker.on('click', cancel_hide);
143
- var hide_timeout = setTimeout(do_hide, 100);
144
- } else {
145
- return this.hide.apply(this, arguments);
146
- }
147
- },
148
-
149
143
  hide: function(e){
150
144
  this.picker.hide();
151
145
  $(window).off('resize', this.place);
@@ -162,15 +156,33 @@
162
156
  });
163
157
  },
164
158
 
159
+ getDate: function() {
160
+ var d = this.getUTCDate();
161
+ return new Date(d.getTime() + (d.getTimezoneOffset()*60000))
162
+ },
163
+
164
+ getUTCDate: function() {
165
+ return this.date;
166
+ },
167
+
168
+ setDate: function(d) {
169
+ this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
170
+ },
171
+
172
+ setUTCDate: function(d) {
173
+ this.date = d;
174
+ this.setValue();
175
+ },
176
+
165
177
  setValue: function() {
166
- var formated = DPGlobal.formatDate(this.date, this.format, this.language);
178
+ var formatted = DPGlobal.formatDate(this.date, this.format, this.language);
167
179
  if (!this.isInput) {
168
180
  if (this.component){
169
- this.element.find('input').prop('value', formated);
181
+ this.element.find('input').prop('value', formatted);
170
182
  }
171
- this.element.data('date', formated);
183
+ this.element.data('date', formatted);
172
184
  } else {
173
- this.element.prop('value', formated);
185
+ this.element.prop('value', formatted);
174
186
  }
175
187
  },
176
188
 
@@ -193,10 +205,14 @@
193
205
  },
194
206
 
195
207
  place: function(){
208
+ var zIndex = parseInt(this.element.parents().filter(function() {
209
+ return $(this).css('z-index') != 'auto';
210
+ }).first().css('z-index'))+10;
196
211
  var offset = this.component ? this.component.offset() : this.element.offset();
197
212
  this.picker.css({
198
213
  top: offset.top + this.height,
199
- left: offset.left
214
+ left: offset.left,
215
+ zIndex: zIndex
200
216
  });
201
217
  },
202
218
 
@@ -236,50 +252,61 @@
236
252
 
237
253
  fill: function() {
238
254
  var d = new Date(this.viewDate),
239
- year = d.getFullYear(),
240
- month = d.getMonth(),
241
- startYear = this.startDate !== -Infinity ? this.startDate.getFullYear() : -Infinity,
242
- startMonth = this.startDate !== -Infinity ? this.startDate.getMonth() : -Infinity,
243
- endYear = this.endDate !== Infinity ? this.endDate.getFullYear() : Infinity,
244
- endMonth = this.endDate !== Infinity ? this.endDate.getMonth() : Infinity,
245
- currentDate = this.date.valueOf();
246
- this.picker.find('.datepicker-days th:eq(1)')
255
+ year = d.getUTCFullYear(),
256
+ month = d.getUTCMonth(),
257
+ startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
258
+ startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
259
+ endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
260
+ endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
261
+ currentDate = this.date.valueOf(),
262
+ today = new Date();
263
+ this.picker.find('.datepicker-days thead th:eq(1)')
247
264
  .text(dates[this.language].months[month]+' '+year);
265
+ this.picker.find('tfoot th.today')
266
+ .text(dates[this.language].today)
267
+ .toggle(this.todayBtn);
248
268
  this.updateNavArrows();
249
269
  this.fillMonths();
250
- var prevMonth = new Date(year, month-1, 28,0,0,0,0),
251
- day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
252
- prevMonth.setDate(day);
253
- prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
270
+ var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
271
+ day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
272
+ prevMonth.setUTCDate(day);
273
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
254
274
  var nextMonth = new Date(prevMonth);
255
- nextMonth.setDate(nextMonth.getDate() + 42);
275
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
256
276
  nextMonth = nextMonth.valueOf();
257
- html = [];
277
+ var html = [];
258
278
  var clsName;
259
279
  while(prevMonth.valueOf() < nextMonth) {
260
- if (prevMonth.getDay() == this.weekStart) {
280
+ if (prevMonth.getUTCDay() == this.weekStart) {
261
281
  html.push('<tr>');
262
282
  }
263
283
  clsName = '';
264
- if (prevMonth.getFullYear() < year || (prevMonth.getFullYear() == year && prevMonth.getMonth() < month)) {
284
+ if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
265
285
  clsName += ' old';
266
- } else if (prevMonth.getFullYear() > year || (prevMonth.getFullYear() == year && prevMonth.getMonth() > month)) {
286
+ } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
267
287
  clsName += ' new';
268
288
  }
289
+ // Compare internal UTC date with local today, not UTC today
290
+ if (this.todayHighlight &&
291
+ prevMonth.getUTCFullYear() == today.getFullYear() &&
292
+ prevMonth.getUTCMonth() == today.getMonth() &&
293
+ prevMonth.getUTCDate() == today.getDate()) {
294
+ clsName += ' today';
295
+ }
269
296
  if (prevMonth.valueOf() == currentDate) {
270
297
  clsName += ' active';
271
298
  }
272
299
  if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) {
273
300
  clsName += ' disabled';
274
301
  }
275
- html.push('<td class="day'+clsName+'">'+prevMonth.getDate() + '</td>');
276
- if (prevMonth.getDay() == this.weekEnd) {
302
+ html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
303
+ if (prevMonth.getUTCDay() == this.weekEnd) {
277
304
  html.push('</tr>');
278
305
  }
279
- prevMonth.setDate(prevMonth.getDate()+1);
306
+ prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
280
307
  }
281
308
  this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
282
- var currentYear = this.date.getFullYear();
309
+ var currentYear = this.date.getUTCFullYear();
283
310
 
284
311
  var months = this.picker.find('.datepicker-months')
285
312
  .find('th:eq(1)')
@@ -287,7 +314,7 @@
287
314
  .end()
288
315
  .find('span').removeClass('active');
289
316
  if (currentYear == year) {
290
- months.eq(this.date.getMonth()).addClass('active');
317
+ months.eq(this.date.getUTCMonth()).addClass('active');
291
318
  }
292
319
  if (year < startYear || year > endYear) {
293
320
  months.addClass('disabled');
@@ -316,16 +343,16 @@
316
343
 
317
344
  updateNavArrows: function() {
318
345
  var d = new Date(this.viewDate),
319
- year = d.getFullYear(),
320
- month = d.getMonth();
346
+ year = d.getUTCFullYear(),
347
+ month = d.getUTCMonth();
321
348
  switch (this.viewMode) {
322
349
  case 0:
323
- if (this.startDate !== -Infinity && year <= this.startDate.getFullYear() && month <= this.startDate.getMonth()) {
350
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
324
351
  this.picker.find('.prev').css({visibility: 'hidden'});
325
352
  } else {
326
353
  this.picker.find('.prev').css({visibility: 'visible'});
327
354
  }
328
- if (this.endDate !== Infinity && year >= this.endDate.getFullYear() && month >= this.endDate.getMonth()) {
355
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
329
356
  this.picker.find('.next').css({visibility: 'hidden'});
330
357
  } else {
331
358
  this.picker.find('.next').css({visibility: 'visible'});
@@ -333,12 +360,12 @@
333
360
  break;
334
361
  case 1:
335
362
  case 2:
336
- if (this.startDate !== -Infinity && year <= this.startDate.getFullYear()) {
363
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
337
364
  this.picker.find('.prev').css({visibility: 'hidden'});
338
365
  } else {
339
366
  this.picker.find('.prev').css({visibility: 'visible'});
340
367
  }
341
- if (this.endDate !== Infinity && year >= this.endDate.getFullYear()) {
368
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
342
369
  this.picker.find('.next').css({visibility: 'hidden'});
343
370
  } else {
344
371
  this.picker.find('.next').css({visibility: 'visible'});
@@ -372,17 +399,36 @@
372
399
  }
373
400
  this.fill();
374
401
  break;
402
+ case 'today':
403
+ var date = new Date();
404
+ date.setUTCHours(0);
405
+ date.setUTCMinutes(0);
406
+ date.setUTCSeconds(0);
407
+ date.setUTCMilliseconds(0);
408
+
409
+ this.showMode(-2);
410
+ var which = this.todayBtn == 'linked' ? null : 'view';
411
+ this._setDate(date, which);
412
+ break;
375
413
  }
376
414
  break;
377
415
  case 'span':
378
416
  if (!target.is('.disabled')) {
379
- this.viewDate.setDate(1);
417
+ this.viewDate.setUTCDate(1);
380
418
  if (target.is('.month')) {
381
419
  var month = target.parent().find('span').index(target);
382
- this.viewDate.setMonth(month);
420
+ this.viewDate.setUTCMonth(month);
421
+ this.element.trigger({
422
+ type: 'changeMonth',
423
+ date: this.viewDate
424
+ });
383
425
  } else {
384
426
  var year = parseInt(target.text(), 10)||0;
385
- this.viewDate.setFullYear(year);
427
+ this.viewDate.setUTCFullYear(year);
428
+ this.element.trigger({
429
+ type: 'changeYear',
430
+ date: this.viewDate
431
+ });
386
432
  }
387
433
  this.showMode(-1);
388
434
  this.fill();
@@ -391,8 +437,8 @@
391
437
  case 'td':
392
438
  if (target.is('.day') && !target.is('.disabled')){
393
439
  var day = parseInt(target.text(), 10)||1;
394
- var year = this.viewDate.getFullYear(),
395
- month = this.viewDate.getMonth();
440
+ var year = this.viewDate.getUTCFullYear(),
441
+ month = this.viewDate.getUTCMonth();
396
442
  if (target.is('.old')) {
397
443
  if (month == 0) {
398
444
  month = 11;
@@ -408,42 +454,43 @@
408
454
  month += 1;
409
455
  }
410
456
  }
411
- this.date = new Date(year, month, day,0,0,0,0);
412
- this.viewDate = new Date(year, month, day,0,0,0,0);
413
- this.fill();
414
- this.setValue();
415
- this.element.trigger({
416
- type: 'changeDate',
417
- date: this.date
418
- });
419
- var element;
420
- if (this.isInput) {
421
- element = this.element;
422
- } else if (this.component){
423
- element = this.element.find('input');
424
- }
425
- if (element) {
426
- element.change();
427
- if (this.autoclose) {
428
- element.blur();
429
- }
430
- }
457
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
431
458
  }
432
459
  break;
433
460
  }
434
461
  }
435
462
  },
436
463
 
437
- mousedown: function(e){
438
- e.stopPropagation();
439
- e.preventDefault();
464
+ _setDate: function(date, which){
465
+ if (!which || which == 'date')
466
+ this.date = date;
467
+ if (!which || which == 'view')
468
+ this.viewDate = date;
469
+ this.fill();
470
+ this.setValue();
471
+ this.element.trigger({
472
+ type: 'changeDate',
473
+ date: this.date
474
+ });
475
+ var element;
476
+ if (this.isInput) {
477
+ element = this.element;
478
+ } else if (this.component){
479
+ element = this.element.find('input');
480
+ }
481
+ if (element) {
482
+ element.change();
483
+ if (this.autoclose) {
484
+ this.hide();
485
+ }
486
+ }
440
487
  },
441
488
 
442
489
  moveMonth: function(date, dir){
443
490
  if (!dir) return date;
444
491
  var new_date = new Date(date.valueOf()),
445
- day = new_date.getDate(),
446
- month = new_date.getMonth(),
492
+ day = new_date.getUTCDate(),
493
+ month = new_date.getUTCMonth(),
447
494
  mag = Math.abs(dir),
448
495
  new_month, test;
449
496
  dir = dir > 0 ? 1 : -1;
@@ -451,12 +498,12 @@
451
498
  test = dir == -1
452
499
  // If going back one month, make sure month is not current month
453
500
  // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
454
- ? function(){ return new_date.getMonth() == month; }
501
+ ? function(){ return new_date.getUTCMonth() == month; }
455
502
  // If going forward one month, make sure month is as expected
456
503
  // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
457
- : function(){ return new_date.getMonth() != new_month; };
504
+ : function(){ return new_date.getUTCMonth() != new_month; };
458
505
  new_month = month + dir;
459
- new_date.setMonth(new_month);
506
+ new_date.setUTCMonth(new_month);
460
507
  // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
461
508
  if (new_month < 0 || new_month > 11)
462
509
  new_month = (new_month + 12) % 12;
@@ -466,15 +513,15 @@
466
513
  // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
467
514
  new_date = this.moveMonth(new_date, dir);
468
515
  // ...then reset the day, keeping it in the new month
469
- new_month = new_date.getMonth();
470
- new_date.setDate(day);
471
- test = function(){ return new_month != new_date.getMonth(); };
516
+ new_month = new_date.getUTCMonth();
517
+ new_date.setUTCDate(day);
518
+ test = function(){ return new_month != new_date.getUTCMonth(); };
472
519
  }
473
520
  // Common date-resetting loop -- if date is beyond end of month, make it
474
521
  // end of month
475
522
  while (test()){
476
- new_date.setDate(--day);
477
- new_date.setMonth(new_month);
523
+ new_date.setUTCDate(--day);
524
+ new_date.setUTCMonth(new_month);
478
525
  }
479
526
  return new_date;
480
527
  },
@@ -483,6 +530,10 @@
483
530
  return this.moveMonth(date, dir*12);
484
531
  },
485
532
 
533
+ dateWithinRange: function(date){
534
+ return date >= this.startDate && date <= this.endDate;
535
+ },
536
+
486
537
  keydown: function(e){
487
538
  if (this.picker.is(':not(:visible)')){
488
539
  if (e.keyCode == 27) // allow escape to hide and re-show picker
@@ -490,7 +541,8 @@
490
541
  return;
491
542
  }
492
543
  var dateChanged = false,
493
- dir, day, month;
544
+ dir, day, month,
545
+ newDate, newViewDate;
494
546
  switch(e.keyCode){
495
547
  case 27: // escape
496
548
  this.hide();
@@ -498,44 +550,61 @@
498
550
  break;
499
551
  case 37: // left
500
552
  case 39: // right
553
+ if (!this.keyboardNavigation) break;
501
554
  dir = e.keyCode == 37 ? -1 : 1;
502
555
  if (e.ctrlKey){
503
- this.date = this.moveYear(this.date, dir);
504
- this.viewDate = this.moveYear(this.viewDate, dir);
556
+ newDate = this.moveYear(this.date, dir);
557
+ newViewDate = this.moveYear(this.viewDate, dir);
505
558
  } else if (e.shiftKey){
506
- this.date = this.moveMonth(this.date, dir);
507
- this.viewDate = this.moveMonth(this.viewDate, dir);
559
+ newDate = this.moveMonth(this.date, dir);
560
+ newViewDate = this.moveMonth(this.viewDate, dir);
508
561
  } else {
509
- this.date.setDate(this.date.getDate() + dir);
510
- this.viewDate.setDate(this.viewDate.getDate() + dir);
562
+ newDate = new Date(this.date);
563
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
564
+ newViewDate = new Date(this.viewDate);
565
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
566
+ }
567
+ if (this.dateWithinRange(newDate)){
568
+ this.date = newDate;
569
+ this.viewDate = newViewDate;
570
+ this.setValue();
571
+ this.update();
572
+ e.preventDefault();
573
+ dateChanged = true;
511
574
  }
512
- this.setValue();
513
- this.update();
514
- e.preventDefault();
515
- dateChanged = true;
516
575
  break;
517
576
  case 38: // up
518
577
  case 40: // down
578
+ if (!this.keyboardNavigation) break;
519
579
  dir = e.keyCode == 38 ? -1 : 1;
520
580
  if (e.ctrlKey){
521
- this.date = this.moveYear(this.date, dir);
522
- this.viewDate = this.moveYear(this.viewDate, dir);
581
+ newDate = this.moveYear(this.date, dir);
582
+ newViewDate = this.moveYear(this.viewDate, dir);
523
583
  } else if (e.shiftKey){
524
- this.date = this.moveMonth(this.date, dir);
525
- this.viewDate = this.moveMonth(this.viewDate, dir);
584
+ newDate = this.moveMonth(this.date, dir);
585
+ newViewDate = this.moveMonth(this.viewDate, dir);
526
586
  } else {
527
- this.date.setDate(this.date.getDate() + dir * 7);
528
- this.viewDate.setDate(this.viewDate.getDate() + dir * 7);
587
+ newDate = new Date(this.date);
588
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
589
+ newViewDate = new Date(this.viewDate);
590
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
591
+ }
592
+ if (this.dateWithinRange(newDate)){
593
+ this.date = newDate;
594
+ this.viewDate = newViewDate;
595
+ this.setValue();
596
+ this.update();
597
+ e.preventDefault();
598
+ dateChanged = true;
529
599
  }
530
- this.setValue();
531
- this.update();
532
- e.preventDefault();
533
- dateChanged = true;
534
600
  break;
535
601
  case 13: // enter
536
602
  this.hide();
537
603
  e.preventDefault();
538
604
  break;
605
+ case 9: // tab
606
+ this.hide();
607
+ break;
539
608
  }
540
609
  if (dateChanged){
541
610
  this.element.trigger({
@@ -573,7 +642,9 @@
573
642
  if (!data) {
574
643
  $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
575
644
  }
576
- if (typeof option == 'string') data[option].apply(data, args);
645
+ if (typeof option == 'string' && typeof data[option] == 'function') {
646
+ data[option].apply(data, args);
647
+ }
577
648
  });
578
649
  };
579
650
 
@@ -586,7 +657,8 @@
586
657
  daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
587
658
  daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
588
659
  months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
589
- monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
660
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
661
+ today: "Today"
590
662
  }
591
663
  }
592
664
 
@@ -637,68 +709,72 @@
637
709
  dir = parseInt(part[1]);
638
710
  switch(part[2]){
639
711
  case 'd':
640
- date.setDate(date.getDate() + dir);
712
+ date.setUTCDate(date.getUTCDate() + dir);
641
713
  break;
642
714
  case 'm':
643
715
  date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
644
716
  break;
645
717
  case 'w':
646
- date.setDate(date.getDate() + dir * 7);
718
+ date.setUTCDate(date.getUTCDate() + dir * 7);
647
719
  break;
648
720
  case 'y':
649
721
  date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
650
722
  break;
651
723
  }
652
724
  }
653
- return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
725
+ return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
654
726
  }
655
- var parts = date ? date.match(this.nonpunctuation) : [],
727
+ var parts = date && date.match(this.nonpunctuation) || [],
656
728
  date = new Date(),
657
729
  parsed = {},
658
730
  setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
659
731
  setters_map = {
660
- yyyy: function(d,v){ return d.setFullYear(v); },
661
- yy: function(d,v){ return d.setFullYear(2000+v); },
732
+ yyyy: function(d,v){ return d.setUTCFullYear(v); },
733
+ yy: function(d,v){ return d.setUTCFullYear(2000+v); },
662
734
  m: function(d,v){
663
735
  v -= 1;
664
- d.setMonth(v);
665
- while (d.getMonth() != v)
666
- d.setDate(d.getDate()-1);
736
+ while (v<0) v += 12;
737
+ v %= 12;
738
+ d.setUTCMonth(v);
739
+ while (d.getUTCMonth() != v)
740
+ d.setUTCDate(d.getUTCDate()-1);
667
741
  return d;
668
742
  },
669
- d: function(d,v){ return d.setDate(v); }
743
+ d: function(d,v){ return d.setUTCDate(v); }
670
744
  },
671
745
  val, filtered, part;
672
746
  setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
673
747
  setters_map['dd'] = setters_map['d'];
674
- date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
748
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
675
749
  if (parts.length == format.parts.length) {
676
750
  for (var i=0, cnt = format.parts.length; i < cnt; i++) {
677
- val = parseInt(parts[i], 10)||1;
751
+ val = parseInt(parts[i], 10);
678
752
  part = format.parts[i];
679
- switch(part) {
680
- case 'MM':
681
- filtered = $(dates[language].months).filter(function(){
682
- var m = this.slice(0, parts[i].length),
683
- p = parts[i].slice(0, m.length);
684
- return m == p;
685
- });
686
- val = $.inArray(filtered[0], dates[language].months) + 1;
687
- break;
688
- case 'M':
689
- filtered = $(dates[language].monthsShort).filter(function(){
690
- var m = this.slice(0, parts[i].length),
691
- p = parts[i].slice(0, m.length);
692
- return m == p;
693
- });
694
- val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
695
- break;
753
+ if (isNaN(val)) {
754
+ switch(part) {
755
+ case 'MM':
756
+ filtered = $(dates[language].months).filter(function(){
757
+ var m = this.slice(0, parts[i].length),
758
+ p = parts[i].slice(0, m.length);
759
+ return m == p;
760
+ });
761
+ val = $.inArray(filtered[0], dates[language].months) + 1;
762
+ break;
763
+ case 'M':
764
+ filtered = $(dates[language].monthsShort).filter(function(){
765
+ var m = this.slice(0, parts[i].length),
766
+ p = parts[i].slice(0, m.length);
767
+ return m == p;
768
+ });
769
+ val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
770
+ break;
771
+ }
696
772
  }
697
773
  parsed[part] = val;
698
774
  }
699
775
  for (var i=0, s; i<setters_order.length; i++){
700
776
  s = setters_order[i];
701
- if (s in parsed)
777
+ if (s in parsed && !isNaN(parsed[s]))
702
778
  setters_map[s](date, parsed[s])
703
779
  }
704
780
  }
@@ -706,12 +782,12 @@
706
782
  },
707
783
  formatDate: function(date, format, language){
708
784
  var val = {
709
- d: date.getDate(),
710
- m: date.getMonth() + 1,
711
- M: dates[language].monthsShort[date.getMonth()],
712
- MM: dates[language].months[date.getMonth()],
713
- yy: date.getFullYear().toString().substring(2),
714
- yyyy: date.getFullYear()
785
+ d: date.getUTCDate(),
786
+ m: date.getUTCMonth() + 1,
787
+ M: dates[language].monthsShort[date.getUTCMonth()],
788
+ MM: dates[language].months[date.getUTCMonth()],
789
+ yy: date.getUTCFullYear().toString().substring(2),
790
+ yyyy: date.getUTCFullYear()
715
791
  };
716
792
  val.dd = (val.d < 10 ? '0' : '') + val.d;
717
793
  val.mm = (val.m < 10 ? '0' : '') + val.m;
@@ -731,27 +807,30 @@
731
807
  '<th class="next"><i class="icon-arrow-right"/></th>'+
732
808
  '</tr>'+
733
809
  '</thead>',
734
- contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>'
810
+ contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
811
+ footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
735
812
  };
736
813
  DPGlobal.template = '<div class="datepicker dropdown-menu">'+
737
814
  '<div class="datepicker-days">'+
738
815
  '<table class=" table-condensed">'+
739
816
  DPGlobal.headTemplate+
740
817
  '<tbody></tbody>'+
818
+ DPGlobal.footTemplate+
741
819
  '</table>'+
742
820
  '</div>'+
743
821
  '<div class="datepicker-months">'+
744
822
  '<table class="table-condensed">'+
745
823
  DPGlobal.headTemplate+
746
824
  DPGlobal.contTemplate+
825
+ DPGlobal.footTemplate+
747
826
  '</table>'+
748
827
  '</div>'+
749
828
  '<div class="datepicker-years">'+
750
829
  '<table class="table-condensed">'+
751
830
  DPGlobal.headTemplate+
752
831
  DPGlobal.contTemplate+
832
+ DPGlobal.footTemplate+
753
833
  '</table>'+
754
834
  '</div>'+
755
835
  '</div>';
756
-
757
- }( window.jQuery )
836
+ }( window.jQuery );