datetimepicker 0.0.2 → 0.0.3

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.
@@ -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: []