datetimepicker 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,8 +5,6 @@ jQuery () ->
5
5
 
6
6
  # This format much match up with the format in Time::DATE_FORMATS[:datetimepicker_date]
7
7
  # TODO: automate this conversion for the gemfile drop in version.
8
- jQuery(".datetimepicker_date").datepicker(format: "yyyy/mm/dd")
9
- .on "changeDate", (ev) ->
10
- $(this).datepicker('hide').blur()
11
-
8
+ jQuery(".datetimepicker_date").datepicker(format: "yyyy/mm/dd", autoclose: true)
9
+
12
10
  jQuery(".datetimepicker_time").timepicker(showMeridian: false, template: "modal", defaultTime: "value")
@@ -1,8 +1,9 @@
1
1
  /* =========================================================
2
- * bootstrap-datepicker.js
2
+ * bootstrap-datepicker.js
3
3
  * http://www.eyecon.ro/bootstrap-datepicker
4
4
  * =========================================================
5
5
  * Copyright 2012 Stefan Petre
6
+ * Improvements by Andrew Rowls
6
7
  *
7
8
  * Licensed under the Apache License, Version 2.0 (the "License");
8
9
  * you may not use this file except in compliance with the License.
@@ -16,177 +17,279 @@
16
17
  * See the License for the specific language governing permissions and
17
18
  * limitations under the License.
18
19
  * ========================================================= */
19
-
20
+
20
21
  !function( $ ) {
21
-
22
+
23
+ function UTCDate(){
24
+ return new Date(Date.UTC.apply(Date, arguments));
25
+ }
26
+
22
27
  // Picker object
23
-
24
- var Datepicker = function(element, options){
28
+
29
+ var Datepicker = function(element, options) {
30
+ var that = this;
31
+
25
32
  this.element = $(element);
33
+ this.language = options.language||this.element.data('date-language')||"en";
34
+ this.language = this.language in dates ? this.language : "en";
26
35
  this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
27
36
  this.picker = $(DPGlobal.template)
28
37
  .appendTo('body')
29
38
  .on({
30
- click: $.proxy(this.click, this),
31
- mousedown: $.proxy(this.mousedown, this)
39
+ click: $.proxy(this.click, this)
32
40
  });
33
41
  this.isInput = this.element.is('input');
34
42
  this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
35
-
43
+ this.hasInput = this.component && this.element.find('input').length;
44
+ if(this.component && this.component.length === 0)
45
+ this.component = false;
46
+
36
47
  if (this.isInput) {
37
48
  this.element.on({
38
49
  focus: $.proxy(this.show, this),
39
- blur: $.proxy(this.hide, this),
40
- keyup: $.proxy(this.update, this)
50
+ keyup: $.proxy(this.update, this),
51
+ keydown: $.proxy(this.keydown, this)
41
52
  });
42
53
  } else {
43
- if (this.component){
54
+ if (this.component && this.hasInput){
55
+ // For components that are not readonly, allow keyboard nav
56
+ this.element.find('input').on({
57
+ focus: $.proxy(this.show, this),
58
+ keyup: $.proxy(this.update, this),
59
+ keydown: $.proxy(this.keydown, this)
60
+ });
61
+
44
62
  this.component.on('click', $.proxy(this.show, this));
45
63
  } else {
46
64
  this.element.on('click', $.proxy(this.show, this));
47
65
  }
48
66
  }
49
-
50
- this.viewMode = 0;
51
- this.weekStart = options.weekStart||this.element.data('date-weekstart')||0;
52
- this.weekEnd = this.weekStart == 0 ? 6 : this.weekStart - 1;
67
+
68
+ $(document).on('mousedown', function (e) {
69
+ // Clicked outside the datepicker, hide it
70
+ if ($(e.target).closest('.datepicker').length == 0) {
71
+ that.hide();
72
+ }
73
+ });
74
+
75
+ this.autoclose = false;
76
+ if ('autoclose' in options) {
77
+ this.autoclose = options.autoclose;
78
+ } else if ('dateAutoclose' in this.element.data()) {
79
+ this.autoclose = this.element.data('date-autoclose');
80
+ }
81
+
82
+ this.keyboardNavigation = true;
83
+ if ('keyboardNavigation' in options) {
84
+ this.keyboardNavigation = options.keyboardNavigation;
85
+ } else if ('dateKeyboardNavigation' in this.element.data()) {
86
+ this.keyboardNavigation = this.element.data('date-keyboard-navigation');
87
+ }
88
+
89
+ switch(options.startView || this.element.data('date-start-view')){
90
+ case 2:
91
+ case 'decade':
92
+ this.viewMode = this.startViewMode = 2;
93
+ break;
94
+ case 1:
95
+ case 'year':
96
+ this.viewMode = this.startViewMode = 1;
97
+ break;
98
+ case 0:
99
+ case 'month':
100
+ default:
101
+ this.viewMode = this.startViewMode = 0;
102
+ break;
103
+ }
104
+
105
+ this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
106
+ this.weekEnd = ((this.weekStart + 6) % 7);
107
+ this.startDate = -Infinity;
108
+ this.endDate = Infinity;
109
+ this.setStartDate(options.startDate||this.element.data('date-startdate'));
110
+ this.setEndDate(options.endDate||this.element.data('date-enddate'));
53
111
  this.fillDow();
54
112
  this.fillMonths();
55
113
  this.update();
56
114
  this.showMode();
57
115
  };
58
-
116
+
59
117
  Datepicker.prototype = {
60
118
  constructor: Datepicker,
61
-
119
+
62
120
  show: function(e) {
63
121
  this.picker.show();
64
122
  this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
123
+ this.update();
65
124
  this.place();
66
125
  $(window).on('resize', $.proxy(this.place, this));
67
126
  if (e ) {
68
127
  e.stopPropagation();
69
128
  e.preventDefault();
70
129
  }
71
- if (!this.isInput) {
72
- $(document).on('mousedown', $.proxy(this.hide, this));
73
- }
74
130
  this.element.trigger({
75
131
  type: 'show',
76
132
  date: this.date
77
133
  });
78
134
  },
79
-
80
- hide: function(){
135
+
136
+ hide: function(e){
81
137
  this.picker.hide();
82
138
  $(window).off('resize', this.place);
83
- this.viewMode = 0;
139
+ this.viewMode = this.startViewMode;
84
140
  this.showMode();
85
141
  if (!this.isInput) {
86
142
  $(document).off('mousedown', this.hide);
87
143
  }
88
- this.setValue();
144
+ if (e && e.currentTarget.value)
145
+ this.setValue();
89
146
  this.element.trigger({
90
147
  type: 'hide',
91
148
  date: this.date
92
149
  });
93
150
  },
94
-
151
+
95
152
  setValue: function() {
96
- var formated = DPGlobal.formatDate(this.date, this.format);
153
+ var formatted = DPGlobal.formatDate(this.date, this.format, this.language);
97
154
  if (!this.isInput) {
98
155
  if (this.component){
99
- this.element.find('input').prop('value', formated);
156
+ this.element.find('input').prop('value', formatted);
100
157
  }
101
- this.element.data('date', formated);
158
+ this.element.data('date', formatted);
102
159
  } else {
103
- this.element.prop('value', formated);
160
+ this.element.prop('value', formatted);
161
+ }
162
+ },
163
+
164
+ setStartDate: function(startDate){
165
+ this.startDate = startDate||-Infinity;
166
+ if (this.startDate !== -Infinity) {
167
+ this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
104
168
  }
169
+ this.update();
170
+ this.updateNavArrows();
105
171
  },
106
-
172
+
173
+ setEndDate: function(endDate){
174
+ this.endDate = endDate||Infinity;
175
+ if (this.endDate !== Infinity) {
176
+ this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
177
+ }
178
+ this.update();
179
+ this.updateNavArrows();
180
+ },
181
+
107
182
  place: function(){
183
+ var zIndex = parseInt(this.element.parents().filter(function() {
184
+ return $(this).css('z-index') != 'auto';
185
+ }).first().css('z-index'))+10;
108
186
  var offset = this.component ? this.component.offset() : this.element.offset();
109
187
  this.picker.css({
110
188
  top: offset.top + this.height,
111
- left: offset.left
189
+ left: offset.left,
190
+ zIndex: zIndex
112
191
  });
113
192
  },
114
-
193
+
115
194
  update: function(){
116
195
  this.date = DPGlobal.parseDate(
117
- this.isInput ? this.element.prop('value') : this.element.data('date'),
118
- this.format
196
+ this.isInput ? this.element.prop('value') : this.element.data('date') || this.element.find('input').prop('value'),
197
+ this.format, this.language
119
198
  );
120
- this.viewDate = new Date(this.date);
199
+ if (this.date < this.startDate) {
200
+ this.viewDate = new Date(this.startDate);
201
+ } else if (this.date > this.endDate) {
202
+ this.viewDate = new Date(this.endDate);
203
+ } else {
204
+ this.viewDate = new Date(this.date);
205
+ }
121
206
  this.fill();
122
207
  },
123
-
208
+
124
209
  fillDow: function(){
125
210
  var dowCnt = this.weekStart;
126
211
  var html = '<tr>';
127
212
  while (dowCnt < this.weekStart + 7) {
128
- html += '<th class="dow">'+DPGlobal.dates.daysMin[(dowCnt++)%7]+'</th>';
213
+ html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
129
214
  }
130
215
  html += '</tr>';
131
216
  this.picker.find('.datepicker-days thead').append(html);
132
217
  },
133
-
218
+
134
219
  fillMonths: function(){
135
220
  var html = '';
136
221
  var i = 0
137
222
  while (i < 12) {
138
- html += '<span class="month">'+DPGlobal.dates.monthsShort[i++]+'</span>';
223
+ html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>';
139
224
  }
140
- this.picker.find('.datepicker-months td').append(html);
225
+ this.picker.find('.datepicker-months td').html(html);
141
226
  },
142
-
227
+
143
228
  fill: function() {
144
229
  var d = new Date(this.viewDate),
145
- year = d.getFullYear(),
146
- month = d.getMonth(),
230
+ year = d.getUTCFullYear(),
231
+ month = d.getUTCMonth(),
232
+ startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
233
+ startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
234
+ endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
235
+ endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
147
236
  currentDate = this.date.valueOf();
148
237
  this.picker.find('.datepicker-days th:eq(1)')
149
- .text(DPGlobal.dates.months[month]+' '+year);
150
- var prevMonth = new Date(year, month-1, 28,0,0,0,0),
151
- day = DPGlobal.getDaysInMonth(prevMonth.getFullYear(), prevMonth.getMonth());
152
- prevMonth.setDate(day);
153
- prevMonth.setDate(day - (prevMonth.getDay() - this.weekStart + 7)%7);
238
+ .text(dates[this.language].months[month]+' '+year);
239
+ this.updateNavArrows();
240
+ this.fillMonths();
241
+ var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
242
+ day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
243
+ prevMonth.setUTCDate(day);
244
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
154
245
  var nextMonth = new Date(prevMonth);
155
- nextMonth.setDate(nextMonth.getDate() + 42);
246
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
156
247
  nextMonth = nextMonth.valueOf();
157
- html = [];
248
+ var html = [];
158
249
  var clsName;
159
250
  while(prevMonth.valueOf() < nextMonth) {
160
- if (prevMonth.getDay() == this.weekStart) {
251
+ if (prevMonth.getUTCDay() == this.weekStart) {
161
252
  html.push('<tr>');
162
253
  }
163
254
  clsName = '';
164
- if (prevMonth.getMonth() < month) {
255
+ if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
165
256
  clsName += ' old';
166
- } else if (prevMonth.getMonth() > month) {
257
+ } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
167
258
  clsName += ' new';
168
259
  }
169
260
  if (prevMonth.valueOf() == currentDate) {
170
261
  clsName += ' active';
171
262
  }
172
- html.push('<td class="day'+clsName+'">'+prevMonth.getDate() + '</td>');
173
- if (prevMonth.getDay() == this.weekEnd) {
263
+ if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) {
264
+ clsName += ' disabled';
265
+ }
266
+ html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
267
+ if (prevMonth.getUTCDay() == this.weekEnd) {
174
268
  html.push('</tr>');
175
269
  }
176
- prevMonth.setDate(prevMonth.getDate()+1);
270
+ prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
177
271
  }
178
272
  this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
179
- var currentYear = this.date.getFullYear();
180
-
273
+ var currentYear = this.date.getUTCFullYear();
274
+
181
275
  var months = this.picker.find('.datepicker-months')
182
276
  .find('th:eq(1)')
183
277
  .text(year)
184
278
  .end()
185
279
  .find('span').removeClass('active');
186
280
  if (currentYear == year) {
187
- months.eq(this.date.getMonth()).addClass('active');
281
+ months.eq(this.date.getUTCMonth()).addClass('active');
282
+ }
283
+ if (year < startYear || year > endYear) {
284
+ months.addClass('disabled');
188
285
  }
189
-
286
+ if (year == startYear) {
287
+ months.slice(0, startMonth).addClass('disabled');
288
+ }
289
+ if (year == endYear) {
290
+ months.slice(endMonth+1).addClass('disabled');
291
+ }
292
+
190
293
  html = '';
191
294
  year = parseInt(year/10, 10) * 10;
192
295
  var yearCont = this.picker.find('.datepicker-years')
@@ -196,12 +299,45 @@
196
299
  .find('td');
197
300
  year -= 1;
198
301
  for (var i = -1; i < 11; i++) {
199
- html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+'">'+year+'</span>';
302
+ html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
200
303
  year += 1;
201
304
  }
202
305
  yearCont.html(html);
203
306
  },
204
-
307
+
308
+ updateNavArrows: function() {
309
+ var d = new Date(this.viewDate),
310
+ year = d.getUTCFullYear(),
311
+ month = d.getUTCMonth();
312
+ switch (this.viewMode) {
313
+ case 0:
314
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
315
+ this.picker.find('.prev').css({visibility: 'hidden'});
316
+ } else {
317
+ this.picker.find('.prev').css({visibility: 'visible'});
318
+ }
319
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
320
+ this.picker.find('.next').css({visibility: 'hidden'});
321
+ } else {
322
+ this.picker.find('.next').css({visibility: 'visible'});
323
+ }
324
+ break;
325
+ case 1:
326
+ case 2:
327
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
328
+ this.picker.find('.prev').css({visibility: 'hidden'});
329
+ } else {
330
+ this.picker.find('.prev').css({visibility: 'visible'});
331
+ }
332
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
333
+ this.picker.find('.next').css({visibility: 'hidden'});
334
+ } else {
335
+ this.picker.find('.next').css({visibility: 'visible'});
336
+ }
337
+ break;
338
+ }
339
+ },
340
+
205
341
  click: function(e) {
206
342
  e.stopPropagation();
207
343
  e.preventDefault();
@@ -215,64 +351,237 @@
215
351
  break;
216
352
  case 'prev':
217
353
  case 'next':
218
- this.viewDate['set'+DPGlobal.modes[this.viewMode].navFnc].call(
219
- this.viewDate,
220
- this.viewDate['get'+DPGlobal.modes[this.viewMode].navFnc].call(this.viewDate) +
221
- DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1)
222
- );
354
+ var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
355
+ switch(this.viewMode){
356
+ case 0:
357
+ this.viewDate = this.moveMonth(this.viewDate, dir);
358
+ break;
359
+ case 1:
360
+ case 2:
361
+ this.viewDate = this.moveYear(this.viewDate, dir);
362
+ break;
363
+ }
223
364
  this.fill();
224
365
  break;
225
366
  }
226
367
  break;
227
368
  case 'span':
228
- if (target.is('.month')) {
229
- var month = target.parent().find('span').index(target);
230
- this.viewDate.setMonth(month);
231
- } else {
232
- var year = parseInt(target.text(), 10)||0;
233
- this.viewDate.setFullYear(year);
369
+ if (!target.is('.disabled')) {
370
+ this.viewDate.setUTCDate(1);
371
+ if (target.is('.month')) {
372
+ var month = target.parent().find('span').index(target);
373
+ this.viewDate.setUTCMonth(month);
374
+ this.element.trigger({
375
+ type: 'changeMonth',
376
+ date: this.viewDate
377
+ });
378
+ } else {
379
+ var year = parseInt(target.text(), 10)||0;
380
+ this.viewDate.setUTCFullYear(year);
381
+ this.element.trigger({
382
+ type: 'changeYear',
383
+ date: this.viewDate
384
+ });
385
+ }
386
+ this.showMode(-1);
387
+ this.fill();
234
388
  }
235
- this.showMode(-1);
236
- this.fill();
237
389
  break;
238
390
  case 'td':
239
- if (target.is('.day')){
391
+ if (target.is('.day') && !target.is('.disabled')){
240
392
  var day = parseInt(target.text(), 10)||1;
241
- var month = this.viewDate.getMonth();
393
+ var year = this.viewDate.getUTCFullYear(),
394
+ month = this.viewDate.getUTCMonth();
242
395
  if (target.is('.old')) {
243
- month -= 1;
396
+ if (month == 0) {
397
+ month = 11;
398
+ year -= 1;
399
+ } else {
400
+ month -= 1;
401
+ }
244
402
  } else if (target.is('.new')) {
245
- month += 1;
403
+ if (month == 11) {
404
+ month = 0;
405
+ year += 1;
406
+ } else {
407
+ month += 1;
408
+ }
246
409
  }
247
- var year = this.viewDate.getFullYear();
248
- this.date = new Date(year, month, day,0,0,0,0);
249
- this.viewDate = new Date(year, month, day,0,0,0,0);
410
+ this.date = UTCDate(year, month, day,0,0,0,0);
411
+ this.viewDate = UTCDate(year, month, day,0,0,0,0);
250
412
  this.fill();
251
413
  this.setValue();
252
414
  this.element.trigger({
253
415
  type: 'changeDate',
254
416
  date: this.date
255
417
  });
418
+ var element;
419
+ if (this.isInput) {
420
+ element = this.element;
421
+ } else if (this.component){
422
+ element = this.element.find('input');
423
+ }
424
+ if (element) {
425
+ element.change();
426
+ if (this.autoclose) {
427
+ this.hide();
428
+ }
429
+ }
256
430
  }
257
431
  break;
258
432
  }
259
433
  }
260
434
  },
261
-
262
- mousedown: function(e){
263
- e.stopPropagation();
264
- e.preventDefault();
435
+
436
+ moveMonth: function(date, dir){
437
+ if (!dir) return date;
438
+ var new_date = new Date(date.valueOf()),
439
+ day = new_date.getUTCDate(),
440
+ month = new_date.getUTCMonth(),
441
+ mag = Math.abs(dir),
442
+ new_month, test;
443
+ dir = dir > 0 ? 1 : -1;
444
+ if (mag == 1){
445
+ test = dir == -1
446
+ // If going back one month, make sure month is not current month
447
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
448
+ ? function(){ return new_date.getUTCMonth() == month; }
449
+ // If going forward one month, make sure month is as expected
450
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
451
+ : function(){ return new_date.getUTCMonth() != new_month; };
452
+ new_month = month + dir;
453
+ new_date.setUTCMonth(new_month);
454
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
455
+ if (new_month < 0 || new_month > 11)
456
+ new_month = (new_month + 12) % 12;
457
+ } else {
458
+ // For magnitudes >1, move one month at a time...
459
+ for (var i=0; i<mag; i++)
460
+ // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
461
+ new_date = this.moveMonth(new_date, dir);
462
+ // ...then reset the day, keeping it in the new month
463
+ new_month = new_date.getUTCMonth();
464
+ new_date.setUTCDate(day);
465
+ test = function(){ return new_month != new_date.getUTCMonth(); };
466
+ }
467
+ // Common date-resetting loop -- if date is beyond end of month, make it
468
+ // end of month
469
+ while (test()){
470
+ new_date.setUTCDate(--day);
471
+ new_date.setUTCMonth(new_month);
472
+ }
473
+ return new_date;
474
+ },
475
+
476
+ moveYear: function(date, dir){
477
+ return this.moveMonth(date, dir*12);
478
+ },
479
+
480
+ dateWithinRange: function(date){
481
+ return date >= this.startDate && date <= this.endDate;
265
482
  },
266
-
483
+
484
+ keydown: function(e){
485
+ if (this.picker.is(':not(:visible)')){
486
+ if (e.keyCode == 27) // allow escape to hide and re-show picker
487
+ this.show();
488
+ return;
489
+ }
490
+ var dateChanged = false,
491
+ dir, day, month,
492
+ newDate, newViewDate;
493
+ switch(e.keyCode){
494
+ case 27: // escape
495
+ this.hide();
496
+ e.preventDefault();
497
+ break;
498
+ case 37: // left
499
+ case 39: // right
500
+ if (!this.keyboardNavigation) break;
501
+ dir = e.keyCode == 37 ? -1 : 1;
502
+ if (e.ctrlKey){
503
+ newDate = this.moveYear(this.date, dir);
504
+ newViewDate = this.moveYear(this.viewDate, dir);
505
+ } else if (e.shiftKey){
506
+ newDate = this.moveMonth(this.date, dir);
507
+ newViewDate = this.moveMonth(this.viewDate, dir);
508
+ } else {
509
+ newDate = new Date(this.date);
510
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
511
+ newViewDate = new Date(this.viewDate);
512
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
513
+ }
514
+ if (this.dateWithinRange(newDate)){
515
+ this.date = newDate;
516
+ this.viewDate = newViewDate;
517
+ this.setValue();
518
+ this.update();
519
+ e.preventDefault();
520
+ dateChanged = true;
521
+ }
522
+ break;
523
+ case 38: // up
524
+ case 40: // down
525
+ if (!this.keyboardNavigation) break;
526
+ dir = e.keyCode == 38 ? -1 : 1;
527
+ if (e.ctrlKey){
528
+ newDate = this.moveYear(this.date, dir);
529
+ newViewDate = this.moveYear(this.viewDate, dir);
530
+ } else if (e.shiftKey){
531
+ newDate = this.moveMonth(this.date, dir);
532
+ newViewDate = this.moveMonth(this.viewDate, dir);
533
+ } else {
534
+ newDate = new Date(this.date);
535
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
536
+ newViewDate = new Date(this.viewDate);
537
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
538
+ }
539
+ if (this.dateWithinRange(newDate)){
540
+ this.date = newDate;
541
+ this.viewDate = newViewDate;
542
+ this.setValue();
543
+ this.update();
544
+ e.preventDefault();
545
+ dateChanged = true;
546
+ }
547
+ break;
548
+ case 13: // enter
549
+ this.hide();
550
+ e.preventDefault();
551
+ break;
552
+ case 9: // tab
553
+ this.hide();
554
+ break;
555
+ }
556
+ if (dateChanged){
557
+ this.element.trigger({
558
+ type: 'changeDate',
559
+ date: this.date
560
+ });
561
+ var element;
562
+ if (this.isInput) {
563
+ element = this.element;
564
+ } else if (this.component){
565
+ element = this.element.find('input');
566
+ }
567
+ if (element) {
568
+ element.change();
569
+ }
570
+ }
571
+ },
572
+
267
573
  showMode: function(dir) {
268
574
  if (dir) {
269
575
  this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
270
576
  }
271
577
  this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
578
+ this.updateNavArrows();
272
579
  }
273
580
  };
274
-
581
+
275
582
  $.fn.datepicker = function ( option ) {
583
+ var args = Array.apply(null, arguments);
584
+ args.shift();
276
585
  return this.each(function () {
277
586
  var $this = $(this),
278
587
  data = $this.data('datepicker'),
@@ -280,14 +589,25 @@
280
589
  if (!data) {
281
590
  $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
282
591
  }
283
- if (typeof option == 'string') data[option]();
592
+ if (typeof option == 'string' && typeof data[option] == 'function') {
593
+ data[option].apply(data, args);
594
+ }
284
595
  });
285
596
  };
286
597
 
287
598
  $.fn.datepicker.defaults = {
288
599
  };
289
600
  $.fn.datepicker.Constructor = Datepicker;
290
-
601
+ var dates = $.fn.datepicker.dates = {
602
+ en: {
603
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
604
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
605
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
606
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
607
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
608
+ }
609
+ }
610
+
291
611
  var DPGlobal = {
292
612
  modes: [
293
613
  {
@@ -305,68 +625,126 @@
305
625
  navFnc: 'FullYear',
306
626
  navStep: 10
307
627
  }],
308
- dates:{
309
- days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
310
- daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
311
- daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
312
- months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
313
- monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
314
- },
315
628
  isLeapYear: function (year) {
316
629
  return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
317
630
  },
318
631
  getDaysInMonth: function (year, month) {
319
632
  return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
320
633
  },
634
+ validParts: /dd?|mm?|MM?|yy(?:yy)?/g,
635
+ nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\r]+/g,
321
636
  parseFormat: function(format){
322
- var separator = format.match(/[.\/-].*?/),
323
- parts = format.split(/\W+/);
324
- if (!separator || !parts || parts.length == 0){
637
+ // IE treats \0 as a string end in inputs (truncating the value),
638
+ // so it's a bad format delimiter, anyway
639
+ var separators = format.replace(this.validParts, '\0').split('\0'),
640
+ parts = format.match(this.validParts);
641
+ if (!separators || !separators.length || !parts || parts.length == 0){
325
642
  throw new Error("Invalid date format.");
326
643
  }
327
- return {separator: separator, parts: parts};
644
+ return {separators: separators, parts: parts};
328
645
  },
329
- parseDate: function(date, format) {
330
- var parts = date.split(format.separator),
331
- date = new Date(1970, 1, 1, 0, 0, 0),
332
- val;
333
- if (parts.length == format.parts.length) {
334
- for (var i=0, cnt = format.parts.length; i < cnt; i++) {
335
- val = parseInt(parts[i], 10)||1;
336
- switch(format.parts[i]) {
337
- case 'dd':
646
+ parseDate: function(date, format, language) {
647
+ if (date instanceof Date) return date;
648
+ if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) {
649
+ var part_re = /([-+]\d+)([dmwy])/,
650
+ parts = date.match(/([-+]\d+)([dmwy])/g),
651
+ part, dir;
652
+ date = new Date();
653
+ for (var i=0; i<parts.length; i++) {
654
+ part = part_re.exec(parts[i]);
655
+ dir = parseInt(part[1]);
656
+ switch(part[2]){
338
657
  case 'd':
339
- date.setDate(val);
658
+ date.setUTCDate(date.getUTCDate() + dir);
340
659
  break;
341
- case 'mm':
342
660
  case 'm':
343
- date.setMonth(val - 1);
661
+ date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
344
662
  break;
345
- case 'yy':
346
- date.setFullYear(2000 + val);
663
+ case 'w':
664
+ date.setUTCDate(date.getUTCDate() + dir * 7);
347
665
  break;
348
- case 'yyyy':
349
- date.setFullYear(val);
666
+ case 'y':
667
+ date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
350
668
  break;
351
669
  }
352
670
  }
671
+ return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
672
+ }
673
+ var parts = date && date.match(this.nonpunctuation) || [],
674
+ date = new Date(),
675
+ parsed = {},
676
+ setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
677
+ setters_map = {
678
+ yyyy: function(d,v){ return d.setUTCFullYear(v); },
679
+ yy: function(d,v){ return d.setUTCFullYear(2000+v); },
680
+ m: function(d,v){
681
+ v -= 1;
682
+ while (v<0) v += 12;
683
+ v %= 12;
684
+ d.setUTCMonth(v);
685
+ while (d.getUTCMonth() != v)
686
+ d.setUTCDate(d.getUTCDate()-1);
687
+ return d;
688
+ },
689
+ d: function(d,v){ return d.setUTCDate(v); }
690
+ },
691
+ val, filtered, part;
692
+ setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
693
+ setters_map['dd'] = setters_map['d'];
694
+ date = UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
695
+ if (parts.length == format.parts.length) {
696
+ for (var i=0, cnt = format.parts.length; i < cnt; i++) {
697
+ val = parseInt(parts[i], 10);
698
+ part = format.parts[i];
699
+ if (isNaN(val)) {
700
+ switch(part) {
701
+ case 'MM':
702
+ filtered = $(dates[language].months).filter(function(){
703
+ var m = this.slice(0, parts[i].length),
704
+ p = parts[i].slice(0, m.length);
705
+ return m == p;
706
+ });
707
+ val = $.inArray(filtered[0], dates[language].months) + 1;
708
+ break;
709
+ case 'M':
710
+ filtered = $(dates[language].monthsShort).filter(function(){
711
+ var m = this.slice(0, parts[i].length),
712
+ p = parts[i].slice(0, m.length);
713
+ return m == p;
714
+ });
715
+ val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
716
+ break;
717
+ }
718
+ }
719
+ parsed[part] = val;
720
+ }
721
+ for (var i=0, s; i<setters_order.length; i++){
722
+ s = setters_order[i];
723
+ if (s in parsed)
724
+ setters_map[s](date, parsed[s])
725
+ }
353
726
  }
354
727
  return date;
355
728
  },
356
- formatDate: function(date, format){
729
+ formatDate: function(date, format, language){
357
730
  var val = {
358
- d: date.getDate(),
359
- m: date.getMonth() + 1,
360
- yy: date.getFullYear().toString().substring(2),
361
- yyyy: date.getFullYear()
731
+ d: date.getUTCDate(),
732
+ m: date.getUTCMonth() + 1,
733
+ M: dates[language].monthsShort[date.getUTCMonth()],
734
+ MM: dates[language].months[date.getUTCMonth()],
735
+ yy: date.getUTCFullYear().toString().substring(2),
736
+ yyyy: date.getUTCFullYear()
362
737
  };
363
738
  val.dd = (val.d < 10 ? '0' : '') + val.d;
364
739
  val.mm = (val.m < 10 ? '0' : '') + val.m;
365
- var date = [];
740
+ var date = [],
741
+ seps = $.extend([], format.separators);
366
742
  for (var i=0, cnt = format.parts.length; i < cnt; i++) {
743
+ if (seps.length)
744
+ date.push(seps.shift())
367
745
  date.push(val[format.parts[i]]);
368
746
  }
369
- return date.join(format.separator);
747
+ return date.join('');
370
748
  },
371
749
  headTemplate: '<thead>'+
372
750
  '<tr>'+
@@ -398,4 +776,4 @@
398
776
  '</div>'+
399
777
  '</div>';
400
778
 
401
- }( window.jQuery )
779
+ }( window.jQuery );
@@ -11,4 +11,4 @@ end
11
11
 
12
12
  require "datetimepicker/date_formats"
13
13
  require "datetimepicker/activerecord"
14
- require "datetimepicker/simple_form/inputs/datetimepicker_input"
14
+ require "datetimepicker/simple_form/inputs"
@@ -0,0 +1,3 @@
1
+ require "datetimepicker/simple_form/inputs/datetimepicker_input"
2
+ require "datetimepicker/simple_form/inputs/datepicker_input"
3
+ require "datetimepicker/simple_form/inputs/timepicker_input"
@@ -0,0 +1,24 @@
1
+ require 'simple_form'
2
+
3
+ module SimpleForm
4
+ module Inputs
5
+
6
+ class DatepickerInput < StringInput
7
+
8
+ def input
9
+ # Remove defaults that we do not want.
10
+ html_classes.delete(:datepicker)
11
+ html_classes.push(:datetimepicker)
12
+
13
+ input_html_classes.push(:datetimepicker_date)
14
+ input_html_options[:type] = "text"
15
+
16
+ # Get the input in the correct format.
17
+ input_html_options[:value] = object.send(attribute_name).try(:strftime, Time::DATE_FORMATS[:datetimepicker_date])
18
+ @builder.text_field(attribute_name, input_html_options)
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'simple_form'
2
+
3
+ module SimpleForm
4
+ module Inputs
5
+
6
+ class TimepickerInput < StringInput
7
+
8
+ def input
9
+ # Remove defaults that we do not want.
10
+ html_classes.delete(:datepicker)
11
+ html_classes.push(:datetimepicker)
12
+
13
+ input_html_classes.push(:datetimepicker_time)
14
+ input_html_options[:type] = "text"
15
+
16
+ # Get the input in the correct format.
17
+ input_html_options[:value] = object.send(attribute_name).try(:strftime, Time::DATE_FORMATS[:datetimepicker_time])
18
+ @builder.text_field(attribute_name, input_html_options)
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module Datetimepicker
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datetimepicker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -35,7 +35,10 @@ files:
35
35
  - lib/datetimepicker/date_and_time_manipulator.rb
36
36
  - lib/datetimepicker/date_formats.rb
37
37
  - lib/datetimepicker/engine.rb
38
+ - lib/datetimepicker/simple_form/inputs.rb
39
+ - lib/datetimepicker/simple_form/inputs/datepicker_input.rb
38
40
  - lib/datetimepicker/simple_form/inputs/datetimepicker_input.rb
41
+ - lib/datetimepicker/simple_form/inputs/timepicker_input.rb
39
42
  - lib/datetimepicker/version.rb
40
43
  homepage: http://github.com/marshill/datetimepicker
41
44
  licenses: []