bootstrap-rails-engine 1.3.0.3 → 1.3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,12 +33,10 @@
33
33
  var Datepicker = function(element, options) {
34
34
  var that = this;
35
35
 
36
+ this._process_options(options);
37
+
36
38
  this.element = $(element);
37
- this.language = options.language||this.element.data('date-language')||"en";
38
- this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de"
39
- this.language = this.language in dates ? this.language : "en";
40
- this.isRTL = dates[this.language].rtl||false;
41
- this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy');
39
+ this.format = DPGlobal.parseFormat(this.o.format);
42
40
  this.isInline = false;
43
41
  this.isInput = this.element.is('input');
44
42
  this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false;
@@ -46,93 +44,26 @@
46
44
  if(this.component && this.component.length === 0)
47
45
  this.component = false;
48
46
 
47
+ this.picker = $(DPGlobal.template);
48
+ this._buildEvents();
49
49
  this._attachEvents();
50
50
 
51
- this.forceParse = true;
52
- if ('forceParse' in options) {
53
- this.forceParse = options.forceParse;
54
- } else if ('dateForceParse' in this.element.data()) {
55
- this.forceParse = this.element.data('date-force-parse');
56
- }
57
-
58
-
59
- this.picker = $(DPGlobal.template)
60
- .appendTo(this.isInline ? this.element : 'body')
61
- .on({
62
- click: $.proxy(this.click, this),
63
- mousedown: $.proxy(this.mousedown, this)
64
- });
65
-
66
51
  if(this.isInline) {
67
- this.picker.addClass('datepicker-inline');
52
+ this.picker.addClass('datepicker-inline').appendTo(this.element);
68
53
  } else {
69
54
  this.picker.addClass('datepicker-dropdown dropdown-menu');
70
55
  }
71
- if (this.isRTL){
56
+
57
+ if (this.o.rtl){
72
58
  this.picker.addClass('datepicker-rtl');
73
59
  this.picker.find('.prev i, .next i')
74
60
  .toggleClass('icon-arrow-left icon-arrow-right');
75
61
  }
76
- $(document).on('mousedown', function (e) {
77
- // Clicked outside the datepicker, hide it
78
- if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) {
79
- that.hide();
80
- }
81
- });
82
62
 
83
- this.autoclose = false;
84
- if ('autoclose' in options) {
85
- this.autoclose = options.autoclose;
86
- } else if ('dateAutoclose' in this.element.data()) {
87
- this.autoclose = this.element.data('date-autoclose');
88
- }
89
63
 
90
- this.keyboardNavigation = true;
91
- if ('keyboardNavigation' in options) {
92
- this.keyboardNavigation = options.keyboardNavigation;
93
- } else if ('dateKeyboardNavigation' in this.element.data()) {
94
- this.keyboardNavigation = this.element.data('date-keyboard-navigation');
95
- }
64
+ this.viewMode = this.o.startView;
96
65
 
97
- this.viewMode = this.startViewMode = 0;
98
- switch(options.startView || this.element.data('date-start-view')){
99
- case 2:
100
- case 'decade':
101
- this.viewMode = this.startViewMode = 2;
102
- break;
103
- case 1:
104
- case 'year':
105
- this.viewMode = this.startViewMode = 1;
106
- break;
107
- }
108
-
109
- this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0;
110
- if (typeof this.minViewMode === 'string') {
111
- switch (this.minViewMode) {
112
- case 'months':
113
- this.minViewMode = 1;
114
- break;
115
- case 'years':
116
- this.minViewMode = 2;
117
- break;
118
- default:
119
- this.minViewMode = 0;
120
- break;
121
- }
122
- }
123
-
124
- this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode);
125
-
126
- this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
127
- this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
128
-
129
- this.calendarWeeks = false;
130
- if ('calendarWeeks' in options) {
131
- this.calendarWeeks = options.calendarWeeks;
132
- } else if ('dateCalendarWeeks' in this.element.data()) {
133
- this.calendarWeeks = this.element.data('date-calendar-weeks');
134
- }
135
- if (this.calendarWeeks)
66
+ if (this.o.calendarWeeks)
136
67
  this.picker.find('tfoot th.today')
137
68
  .attr('colspan', function(i, val){
138
69
  return parseInt(val) + 1;
@@ -140,14 +71,10 @@
140
71
 
141
72
  this._allow_update = false;
142
73
 
143
- this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
144
- this.weekEnd = ((this.weekStart + 6) % 7);
145
- this.startDate = -Infinity;
146
- this.endDate = Infinity;
147
- this.daysOfWeekDisabled = [];
148
- this.setStartDate(options.startDate||this.element.data('date-startdate'));
149
- this.setEndDate(options.endDate||this.element.data('date-enddate'));
150
- this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled'));
74
+ this.setStartDate(this.o.startDate);
75
+ this.setEndDate(this.o.endDate);
76
+ this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
77
+
151
78
  this.fillDow();
152
79
  this.fillMonths();
153
80
 
@@ -164,9 +91,85 @@
164
91
  Datepicker.prototype = {
165
92
  constructor: Datepicker,
166
93
 
94
+ _process_options: function(opts){
95
+ // Store raw options for reference
96
+ this._o = $.extend({}, this._o, opts);
97
+ // Processed options
98
+ var o = this.o = $.extend({}, this._o);
99
+
100
+ // Check if "de-DE" style date is available, if not language should
101
+ // fallback to 2 letter code eg "de"
102
+ var lang = o.language;
103
+ if (!dates[lang]) {
104
+ lang = lang.split('-')[0];
105
+ if (!dates[lang])
106
+ lang = $.fn.datepicker.defaults.language;
107
+ }
108
+ o.language = lang;
109
+
110
+ switch(o.startView){
111
+ case 2:
112
+ case 'decade':
113
+ o.startView = 2;
114
+ break;
115
+ case 1:
116
+ case 'year':
117
+ o.startView = 1;
118
+ break;
119
+ default:
120
+ o.startView = 0;
121
+ }
122
+
123
+ switch (o.minViewMode) {
124
+ case 1:
125
+ case 'months':
126
+ o.minViewMode = 1;
127
+ break;
128
+ case 2:
129
+ case 'years':
130
+ o.minViewMode = 2;
131
+ break;
132
+ default:
133
+ o.minViewMode = 0;
134
+ }
135
+
136
+ o.startView = Math.max(o.startView, o.minViewMode);
137
+
138
+ o.weekStart %= 7;
139
+ o.weekEnd = ((o.weekStart + 6) % 7);
140
+
141
+ var format = DPGlobal.parseFormat(o.format)
142
+ if (o.startDate !== -Infinity) {
143
+ o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
144
+ }
145
+ if (o.endDate !== Infinity) {
146
+ o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
147
+ }
148
+
149
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
150
+ if (!$.isArray(o.daysOfWeekDisabled))
151
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
152
+ o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) {
153
+ return parseInt(d, 10);
154
+ });
155
+ },
167
156
  _events: [],
168
- _attachEvents: function(){
169
- this._detachEvents();
157
+ _secondaryEvents: [],
158
+ _applyEvents: function(evs){
159
+ for (var i=0, el, ev; i<evs.length; i++){
160
+ el = evs[i][0];
161
+ ev = evs[i][1];
162
+ el.on(ev);
163
+ }
164
+ },
165
+ _unapplyEvents: function(evs){
166
+ for (var i=0, el, ev; i<evs.length; i++){
167
+ el = evs[i][0];
168
+ ev = evs[i][1];
169
+ el.off(ev);
170
+ }
171
+ },
172
+ _buildEvents: function(){
170
173
  if (this.isInput) { // single input
171
174
  this._events = [
172
175
  [this.element, {
@@ -189,9 +192,9 @@
189
192
  }]
190
193
  ];
191
194
  }
192
- else if (this.element.is('div')) { // inline datepicker
193
- this.isInline = true;
194
- }
195
+ else if (this.element.is('div')) { // inline datepicker
196
+ this.isInline = true;
197
+ }
195
198
  else {
196
199
  this._events = [
197
200
  [this.element, {
@@ -199,62 +202,95 @@
199
202
  }]
200
203
  ];
201
204
  }
202
- for (var i=0, el, ev; i<this._events.length; i++){
203
- el = this._events[i][0];
204
- ev = this._events[i][1];
205
- el.on(ev);
206
- }
205
+
206
+ this._secondaryEvents = [
207
+ [this.picker, {
208
+ click: $.proxy(this.click, this)
209
+ }],
210
+ [$(window), {
211
+ resize: $.proxy(this.place, this)
212
+ }],
213
+ [$(document), {
214
+ mousedown: $.proxy(function (e) {
215
+ // Clicked outside the datepicker, hide it
216
+ if (!(
217
+ this.element.is(e.target) ||
218
+ this.element.find(e.target).size() ||
219
+ this.picker.is(e.target) ||
220
+ this.picker.find(e.target).size()
221
+ )) {
222
+ this.hide();
223
+ }
224
+ }, this)
225
+ }]
226
+ ];
227
+ },
228
+ _attachEvents: function(){
229
+ this._detachEvents();
230
+ this._applyEvents(this._events);
207
231
  },
208
232
  _detachEvents: function(){
209
- for (var i=0, el, ev; i<this._events.length; i++){
210
- el = this._events[i][0];
211
- ev = this._events[i][1];
212
- el.off(ev);
213
- }
214
- this._events = [];
233
+ this._unapplyEvents(this._events);
234
+ },
235
+ _attachSecondaryEvents: function(){
236
+ this._detachSecondaryEvents();
237
+ this._applyEvents(this._secondaryEvents);
238
+ },
239
+ _detachSecondaryEvents: function(){
240
+ this._unapplyEvents(this._secondaryEvents);
241
+ },
242
+ _trigger: function(event, altdate){
243
+ var date = altdate || this.date,
244
+ local_date = new Date(date.getTime() + (date.getTimezoneOffset()*60000));
245
+
246
+ this.element.trigger({
247
+ type: event,
248
+ date: local_date,
249
+ format: $.proxy(function(altformat){
250
+ var format = this.format;
251
+ if (altformat)
252
+ format = DPGlobal.parseFormat(altformat);
253
+ return DPGlobal.formatDate(date, format, this.language);
254
+ }, this)
255
+ });
215
256
  },
216
257
 
217
258
  show: function(e) {
259
+ if (!this.isInline)
260
+ this.picker.appendTo('body');
218
261
  this.picker.show();
219
262
  this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
220
263
  this.place();
221
- $(window).on('resize', $.proxy(this.place, this));
264
+ this._attachSecondaryEvents();
222
265
  if (e) {
223
266
  e.preventDefault();
224
267
  }
225
- this.element.trigger({
226
- type: 'show',
227
- date: this.date
228
- });
268
+ this._trigger('show');
229
269
  },
230
270
 
231
271
  hide: function(e){
232
272
  if(this.isInline) return;
233
273
  if (!this.picker.is(':visible')) return;
234
- this.picker.hide();
235
- $(window).off('resize', this.place);
236
- this.viewMode = this.startViewMode;
274
+ this.picker.hide().detach();
275
+ this._detachSecondaryEvents();
276
+ this.viewMode = this.o.startView;
237
277
  this.showMode();
238
- if (!this.isInput) {
239
- $(document).off('mousedown', this.hide);
240
- }
241
278
 
242
279
  if (
243
- this.forceParse &&
280
+ this.o.forceParse &&
244
281
  (
245
282
  this.isInput && this.element.val() ||
246
283
  this.hasInput && this.element.find('input').val()
247
284
  )
248
285
  )
249
286
  this.setValue();
250
- this.element.trigger({
251
- type: 'hide',
252
- date: this.date
253
- });
287
+ this._trigger('hide');
254
288
  },
255
289
 
256
290
  remove: function() {
291
+ this.hide();
257
292
  this._detachEvents();
293
+ this._detachSecondaryEvents();
258
294
  this.picker.remove();
259
295
  delete this.element.data().datepicker;
260
296
  if (!this.isInput) {
@@ -286,7 +322,6 @@
286
322
  if (this.component){
287
323
  this.element.find('input').val(formatted);
288
324
  }
289
- this.element.data('date', formatted);
290
325
  } else {
291
326
  this.element.val(formatted);
292
327
  }
@@ -295,35 +330,23 @@
295
330
  getFormattedDate: function(format) {
296
331
  if (format === undefined)
297
332
  format = this.format;
298
- return DPGlobal.formatDate(this.date, format, this.language);
333
+ return DPGlobal.formatDate(this.date, format, this.o.language);
299
334
  },
300
335
 
301
336
  setStartDate: function(startDate){
302
- this.startDate = startDate||-Infinity;
303
- if (this.startDate !== -Infinity) {
304
- this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
305
- }
337
+ this._process_options({startDate: startDate});
306
338
  this.update();
307
339
  this.updateNavArrows();
308
340
  },
309
341
 
310
342
  setEndDate: function(endDate){
311
- this.endDate = endDate||Infinity;
312
- if (this.endDate !== Infinity) {
313
- this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
314
- }
343
+ this._process_options({endDate: endDate});
315
344
  this.update();
316
345
  this.updateNavArrows();
317
346
  },
318
347
 
319
348
  setDaysOfWeekDisabled: function(daysOfWeekDisabled){
320
- this.daysOfWeekDisabled = daysOfWeekDisabled||[];
321
- if (!$.isArray(this.daysOfWeekDisabled)) {
322
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
323
- }
324
- this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
325
- return parseInt(d, 10);
326
- });
349
+ this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
327
350
  this.update();
328
351
  this.updateNavArrows();
329
352
  },
@@ -352,16 +375,17 @@
352
375
  fromArgs = true;
353
376
  } else {
354
377
  date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
378
+ delete this.element.data().date;
355
379
  }
356
380
 
357
- this.date = DPGlobal.parseDate(date, this.format, this.language);
381
+ this.date = DPGlobal.parseDate(date, this.format, this.o.language);
358
382
 
359
383
  if(fromArgs) this.setValue();
360
384
 
361
- if (this.date < this.startDate) {
362
- this.viewDate = new Date(this.startDate);
363
- } else if (this.date > this.endDate) {
364
- this.viewDate = new Date(this.endDate);
385
+ if (this.date < this.o.startDate) {
386
+ this.viewDate = new Date(this.o.startDate);
387
+ } else if (this.date > this.o.endDate) {
388
+ this.viewDate = new Date(this.o.endDate);
365
389
  } else {
366
390
  this.viewDate = new Date(this.date);
367
391
  }
@@ -369,15 +393,15 @@
369
393
  },
370
394
 
371
395
  fillDow: function(){
372
- var dowCnt = this.weekStart,
396
+ var dowCnt = this.o.weekStart,
373
397
  html = '<tr>';
374
- if(this.calendarWeeks){
398
+ if(this.o.calendarWeeks){
375
399
  var cell = '<th class="cw">&nbsp;</th>';
376
400
  html += cell;
377
401
  this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
378
402
  }
379
- while (dowCnt < this.weekStart + 7) {
380
- html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
403
+ while (dowCnt < this.o.weekStart + 7) {
404
+ html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
381
405
  }
382
406
  html += '</tr>';
383
407
  this.picker.find('.datepicker-days thead').append(html);
@@ -387,46 +411,93 @@
387
411
  var html = '',
388
412
  i = 0;
389
413
  while (i < 12) {
390
- html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>';
414
+ html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
391
415
  }
392
416
  this.picker.find('.datepicker-months td').html(html);
393
417
  },
394
418
 
419
+ setRange: function(range){
420
+ if (!range || !range.length)
421
+ delete this.range;
422
+ else
423
+ this.range = $.map(range, function(d){ return d.valueOf(); });
424
+ this.fill();
425
+ },
426
+
427
+ getClassNames: function(date){
428
+ var cls = [],
429
+ year = this.viewDate.getUTCFullYear(),
430
+ month = this.viewDate.getUTCMonth(),
431
+ currentDate = this.date.valueOf(),
432
+ today = new Date();
433
+ if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) {
434
+ cls.push('old');
435
+ } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) {
436
+ cls.push('new');
437
+ }
438
+ // Compare internal UTC date with local today, not UTC today
439
+ if (this.o.todayHighlight &&
440
+ date.getUTCFullYear() == today.getFullYear() &&
441
+ date.getUTCMonth() == today.getMonth() &&
442
+ date.getUTCDate() == today.getDate()) {
443
+ cls.push('today');
444
+ }
445
+ if (currentDate && date.valueOf() == currentDate) {
446
+ cls.push('active');
447
+ }
448
+ if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
449
+ $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) {
450
+ cls.push('disabled');
451
+ }
452
+ if (this.range){
453
+ if (date > this.range[0] && date < this.range[this.range.length-1]){
454
+ cls.push('range');
455
+ }
456
+ if ($.inArray(date.valueOf(), this.range) != -1){
457
+ cls.push('selected');
458
+ }
459
+ }
460
+ return cls;
461
+ },
462
+
395
463
  fill: function() {
396
464
  var d = new Date(this.viewDate),
397
465
  year = d.getUTCFullYear(),
398
466
  month = d.getUTCMonth(),
399
- startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
400
- startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
401
- endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
402
- endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
467
+ startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
468
+ startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
469
+ endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
470
+ endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
403
471
  currentDate = this.date && this.date.valueOf(),
404
- today = new Date();
405
- this.picker.find('.datepicker-days thead th.switch')
406
- .text(dates[this.language].months[month]+' '+year);
472
+ tooltip;
473
+ this.picker.find('.datepicker-days thead th.datepicker-switch')
474
+ .text(dates[this.o.language].months[month]+' '+year);
407
475
  this.picker.find('tfoot th.today')
408
- .text(dates[this.language].today)
409
- .toggle(this.todayBtn !== false);
476
+ .text(dates[this.o.language].today)
477
+ .toggle(this.o.todayBtn !== false);
478
+ this.picker.find('tfoot th.clear')
479
+ .text(dates[this.o.language].clear)
480
+ .toggle(this.o.clearBtn !== false);
410
481
  this.updateNavArrows();
411
482
  this.fillMonths();
412
483
  var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
413
484
  day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
414
485
  prevMonth.setUTCDate(day);
415
- prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
486
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
416
487
  var nextMonth = new Date(prevMonth);
417
488
  nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
418
489
  nextMonth = nextMonth.valueOf();
419
490
  var html = [];
420
491
  var clsName;
421
492
  while(prevMonth.valueOf() < nextMonth) {
422
- if (prevMonth.getUTCDay() == this.weekStart) {
493
+ if (prevMonth.getUTCDay() == this.o.weekStart) {
423
494
  html.push('<tr>');
424
- if(this.calendarWeeks){
495
+ if(this.o.calendarWeeks){
425
496
  // ISO 8601: First week contains first thursday.
426
497
  // ISO also states week starts on Monday, but we can be more abstract here.
427
498
  var
428
499
  // Start of current week: based on weekstart/current date
429
- ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
500
+ ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
430
501
  // Thursday of this week
431
502
  th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
432
503
  // First Thursday of year, year from thursday
@@ -437,28 +508,26 @@
437
508
 
438
509
  }
439
510
  }
440
- clsName = '';
441
- if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
442
- clsName += ' old';
443
- } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
444
- clsName += ' new';
445
- }
446
- // Compare internal UTC date with local today, not UTC today
447
- if (this.todayHighlight &&
448
- prevMonth.getUTCFullYear() == today.getFullYear() &&
449
- prevMonth.getUTCMonth() == today.getMonth() &&
450
- prevMonth.getUTCDate() == today.getDate()) {
451
- clsName += ' today';
452
- }
453
- if (currentDate && prevMonth.valueOf() == currentDate) {
454
- clsName += ' active';
455
- }
456
- if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
457
- $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
458
- clsName += ' disabled';
459
- }
460
- html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
461
- if (prevMonth.getUTCDay() == this.weekEnd) {
511
+ clsName = this.getClassNames(prevMonth);
512
+ clsName.push('day');
513
+
514
+ var before = this.o.beforeShowDay(prevMonth);
515
+ if (before === undefined)
516
+ before = {};
517
+ else if (typeof(before) === 'boolean')
518
+ before = {enabled: before};
519
+ else if (typeof(before) === 'string')
520
+ before = {classes: before};
521
+ if (before.enabled === false)
522
+ clsName.push('disabled');
523
+ if (before.classes)
524
+ clsName = clsName.concat(before.classes.split(/\s+/));
525
+ if (before.tooltip)
526
+ tooltip = before.tooltip;
527
+
528
+ clsName = $.unique(clsName);
529
+ html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
530
+ if (prevMonth.getUTCDay() == this.o.weekEnd) {
462
531
  html.push('</tr>');
463
532
  }
464
533
  prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
@@ -493,7 +562,7 @@
493
562
  .find('td');
494
563
  year -= 1;
495
564
  for (var i = -1; i < 11; i++) {
496
- html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
565
+ html += '<span class="year'+(i == -1 ? ' old' : i == 10 ? ' new' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
497
566
  year += 1;
498
567
  }
499
568
  yearCont.html(html);
@@ -507,12 +576,12 @@
507
576
  month = d.getUTCMonth();
508
577
  switch (this.viewMode) {
509
578
  case 0:
510
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
579
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) {
511
580
  this.picker.find('.prev').css({visibility: 'hidden'});
512
581
  } else {
513
582
  this.picker.find('.prev').css({visibility: 'visible'});
514
583
  }
515
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
584
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) {
516
585
  this.picker.find('.next').css({visibility: 'hidden'});
517
586
  } else {
518
587
  this.picker.find('.next').css({visibility: 'visible'});
@@ -520,12 +589,12 @@
520
589
  break;
521
590
  case 1:
522
591
  case 2:
523
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
592
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) {
524
593
  this.picker.find('.prev').css({visibility: 'hidden'});
525
594
  } else {
526
595
  this.picker.find('.prev').css({visibility: 'visible'});
527
596
  }
528
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
597
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) {
529
598
  this.picker.find('.next').css({visibility: 'hidden'});
530
599
  } else {
531
600
  this.picker.find('.next').css({visibility: 'visible'});
@@ -541,7 +610,7 @@
541
610
  switch(target[0].nodeName.toLowerCase()) {
542
611
  case 'th':
543
612
  switch(target[0].className) {
544
- case 'switch':
613
+ case 'datepicker-switch':
545
614
  this.showMode(1);
546
615
  break;
547
616
  case 'prev':
@@ -563,9 +632,18 @@
563
632
  date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
564
633
 
565
634
  this.showMode(-2);
566
- var which = this.todayBtn == 'linked' ? null : 'view';
635
+ var which = this.o.todayBtn == 'linked' ? null : 'view';
567
636
  this._setDate(date, which);
568
637
  break;
638
+ case 'clear':
639
+ if (this.isInput)
640
+ this.element.val("");
641
+ else
642
+ this.element.find('input').val("");
643
+ this.update();
644
+ if (this.o.autoclose)
645
+ this.hide();
646
+ break;
569
647
  }
570
648
  break;
571
649
  case 'span':
@@ -576,11 +654,8 @@
576
654
  var month = target.parent().find('span').index(target);
577
655
  var year = this.viewDate.getUTCFullYear();
578
656
  this.viewDate.setUTCMonth(month);
579
- this.element.trigger({
580
- type: 'changeMonth',
581
- date: this.viewDate
582
- });
583
- if ( this.minViewMode == 1 ) {
657
+ this._trigger('changeMonth', this.viewDate);
658
+ if (this.o.minViewMode === 1) {
584
659
  this._setDate(UTCDate(year, month, day,0,0,0,0));
585
660
  }
586
661
  } else {
@@ -588,11 +663,8 @@
588
663
  var day = 1;
589
664
  var month = 0;
590
665
  this.viewDate.setUTCFullYear(year);
591
- this.element.trigger({
592
- type: 'changeYear',
593
- date: this.viewDate
594
- });
595
- if ( this.minViewMode == 2 ) {
666
+ this._trigger('changeYear', this.viewDate);
667
+ if (this.o.minViewMode === 2) {
596
668
  this._setDate(UTCDate(year, month, day,0,0,0,0));
597
669
  }
598
670
  }
@@ -629,15 +701,12 @@
629
701
 
630
702
  _setDate: function(date, which){
631
703
  if (!which || which == 'date')
632
- this.date = date;
704
+ this.date = new Date(date);
633
705
  if (!which || which == 'view')
634
- this.viewDate = date;
706
+ this.viewDate = new Date(date);
635
707
  this.fill();
636
708
  this.setValue();
637
- this.element.trigger({
638
- type: 'changeDate',
639
- date: this.date
640
- });
709
+ this._trigger('changeDate');
641
710
  var element;
642
711
  if (this.isInput) {
643
712
  element = this.element;
@@ -646,7 +715,7 @@
646
715
  }
647
716
  if (element) {
648
717
  element.change();
649
- if (this.autoclose && (!which || which == 'date')) {
718
+ if (this.o.autoclose && (!which || which == 'date')) {
650
719
  this.hide();
651
720
  }
652
721
  }
@@ -697,7 +766,7 @@
697
766
  },
698
767
 
699
768
  dateWithinRange: function(date){
700
- return date >= this.startDate && date <= this.endDate;
769
+ return date >= this.o.startDate && date <= this.o.endDate;
701
770
  },
702
771
 
703
772
  keydown: function(e){
@@ -716,7 +785,7 @@
716
785
  break;
717
786
  case 37: // left
718
787
  case 39: // right
719
- if (!this.keyboardNavigation) break;
788
+ if (!this.o.keyboardNavigation) break;
720
789
  dir = e.keyCode == 37 ? -1 : 1;
721
790
  if (e.ctrlKey){
722
791
  newDate = this.moveYear(this.date, dir);
@@ -741,7 +810,7 @@
741
810
  break;
742
811
  case 38: // up
743
812
  case 40: // down
744
- if (!this.keyboardNavigation) break;
813
+ if (!this.o.keyboardNavigation) break;
745
814
  dir = e.keyCode == 38 ? -1 : 1;
746
815
  if (e.ctrlKey){
747
816
  newDate = this.moveYear(this.date, dir);
@@ -773,10 +842,7 @@
773
842
  break;
774
843
  }
775
844
  if (dateChanged){
776
- this.element.trigger({
777
- type: 'changeDate',
778
- date: this.date
779
- });
845
+ this._trigger('changeDate');
780
846
  var element;
781
847
  if (this.isInput) {
782
848
  element = this.element;
@@ -791,7 +857,7 @@
791
857
 
792
858
  showMode: function(dir) {
793
859
  if (dir) {
794
- this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
860
+ this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
795
861
  }
796
862
  /*
797
863
  vitalets: fixing bug of very special conditions:
@@ -808,24 +874,151 @@
808
874
  }
809
875
  };
810
876
 
877
+ var DateRangePicker = function(element, options){
878
+ this.element = $(element);
879
+ this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; });
880
+ delete options.inputs;
881
+
882
+ $(this.inputs)
883
+ .datepicker(options)
884
+ .bind('changeDate', $.proxy(this.dateUpdated, this));
885
+
886
+ this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); });
887
+ this.updateDates();
888
+ };
889
+ DateRangePicker.prototype = {
890
+ updateDates: function(){
891
+ this.dates = $.map(this.pickers, function(i){ return i.date; });
892
+ this.updateRanges();
893
+ },
894
+ updateRanges: function(){
895
+ var range = $.map(this.dates, function(d){ return d.valueOf(); });
896
+ $.each(this.pickers, function(i, p){
897
+ p.setRange(range);
898
+ });
899
+ },
900
+ dateUpdated: function(e){
901
+ var dp = $(e.target).data('datepicker'),
902
+ new_date = dp.getUTCDate(),
903
+ i = $.inArray(e.target, this.inputs),
904
+ l = this.inputs.length;
905
+ if (i == -1) return;
906
+
907
+ if (new_date < this.dates[i]){
908
+ // Date being moved earlier/left
909
+ while (i>=0 && new_date < this.dates[i]){
910
+ this.pickers[i--].setUTCDate(new_date);
911
+ }
912
+ }
913
+ else if (new_date > this.dates[i]){
914
+ // Date being moved later/right
915
+ while (i<l && new_date > this.dates[i]){
916
+ this.pickers[i++].setUTCDate(new_date);
917
+ }
918
+ }
919
+ this.updateDates();
920
+ },
921
+ remove: function(){
922
+ $.map(this.pickers, function(p){ p.remove(); });
923
+ delete this.element.data().datepicker;
924
+ }
925
+ };
926
+
927
+ function opts_from_el(el, prefix){
928
+ // Derive options from element data-attrs
929
+ var data = $(el).data(),
930
+ out = {}, inkey,
931
+ replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'),
932
+ prefix = new RegExp('^' + prefix.toLowerCase());
933
+ for (var key in data)
934
+ if (prefix.test(key)){
935
+ inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); });
936
+ out[inkey] = data[key];
937
+ }
938
+ return out;
939
+ }
940
+
941
+ function opts_from_locale(lang){
942
+ // Derive options from locale plugins
943
+ var out = {};
944
+ // Check if "de-DE" style date is available, if not language should
945
+ // fallback to 2 letter code eg "de"
946
+ if (!dates[lang]) {
947
+ lang = lang.split('-')[0]
948
+ if (!dates[lang])
949
+ return;
950
+ }
951
+ var d = dates[lang];
952
+ $.each($.fn.datepicker.locale_opts, function(i,k){
953
+ if (k in d)
954
+ out[k] = d[k];
955
+ });
956
+ return out;
957
+ }
958
+
959
+ var old = $.fn.datepicker;
811
960
  $.fn.datepicker = function ( option ) {
812
961
  var args = Array.apply(null, arguments);
813
962
  args.shift();
814
- return this.each(function () {
963
+ var internal_return,
964
+ this_return;
965
+ this.each(function () {
815
966
  var $this = $(this),
816
967
  data = $this.data('datepicker'),
817
968
  options = typeof option == 'object' && option;
818
969
  if (!data) {
819
- $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options))));
970
+ var elopts = opts_from_el(this, 'date'),
971
+ // Preliminary otions
972
+ xopts = $.extend({}, $.fn.datepicker.defaults, elopts, options),
973
+ locopts = opts_from_locale(xopts.language),
974
+ // Options priority: js args, data-attrs, locales, defaults
975
+ opts = $.extend({}, $.fn.datepicker.defaults, locopts, elopts, options);
976
+ if ($this.is('.input-daterange') || opts.inputs){
977
+ var ropts = {
978
+ inputs: opts.inputs || $this.find('input').toArray()
979
+ };
980
+ $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
981
+ }
982
+ else{
983
+ $this.data('datepicker', (data = new Datepicker(this, opts)));
984
+ }
820
985
  }
821
986
  if (typeof option == 'string' && typeof data[option] == 'function') {
822
- data[option].apply(data, args);
987
+ internal_return = data[option].apply(data, args);
988
+ if (internal_return !== undefined)
989
+ return false;
823
990
  }
824
991
  });
992
+ if (internal_return !== undefined)
993
+ return internal_return;
994
+ else
995
+ return this;
825
996
  };
826
997
 
827
998
  $.fn.datepicker.defaults = {
999
+ autoclose: false,
1000
+ beforeShowDay: $.noop,
1001
+ calendarWeeks: false,
1002
+ clearBtn: false,
1003
+ daysOfWeekDisabled: [],
1004
+ endDate: Infinity,
1005
+ forceParse: true,
1006
+ format: 'mm/dd/yyyy',
1007
+ keyboardNavigation: true,
1008
+ language: 'en',
1009
+ minViewMode: 0,
1010
+ rtl: false,
1011
+ startDate: -Infinity,
1012
+ startView: 0,
1013
+ todayBtn: false,
1014
+ todayHighlight: false,
1015
+ weekStart: 0
828
1016
  };
1017
+ $.fn.datepicker.locale_opts = [
1018
+ 'format',
1019
+ 'rtl',
1020
+ 'weekStart'
1021
+ ];
829
1022
  $.fn.datepicker.Constructor = Datepicker;
830
1023
  var dates = $.fn.datepicker.dates = {
831
1024
  en: {
@@ -834,7 +1027,8 @@
834
1027
  daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
835
1028
  months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
836
1029
  monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
837
- today: "Today"
1030
+ today: "Today",
1031
+ clear: "Clear"
838
1032
  }
839
1033
  };
840
1034
 
@@ -979,7 +1173,7 @@
979
1173
  val.mm = (val.m < 10 ? '0' : '') + val.m;
980
1174
  var date = [],
981
1175
  seps = $.extend([], format.separators);
982
- for (var i=0, cnt = format.parts.length; i < cnt; i++) {
1176
+ for (var i=0, cnt = format.parts.length; i <= cnt; i++) {
983
1177
  if (seps.length)
984
1178
  date.push(seps.shift());
985
1179
  date.push(val[format.parts[i]]);
@@ -989,12 +1183,12 @@
989
1183
  headTemplate: '<thead>'+
990
1184
  '<tr>'+
991
1185
  '<th class="prev"><i class="icon-arrow-left"/></th>'+
992
- '<th colspan="5" class="switch"></th>'+
1186
+ '<th colspan="5" class="datepicker-switch"></th>'+
993
1187
  '<th class="next"><i class="icon-arrow-right"/></th>'+
994
1188
  '</tr>'+
995
1189
  '</thead>',
996
1190
  contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
997
- footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
1191
+ footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>'
998
1192
  };
999
1193
  DPGlobal.template = '<div class="datepicker">'+
1000
1194
  '<div class="datepicker-days">'+
@@ -1022,4 +1216,32 @@
1022
1216
 
1023
1217
  $.fn.datepicker.DPGlobal = DPGlobal;
1024
1218
 
1219
+
1220
+ /* DATEPICKER NO CONFLICT
1221
+ * =================== */
1222
+
1223
+ $.fn.datepicker.noConflict = function(){
1224
+ $.fn.datepicker = old;
1225
+ return this;
1226
+ };
1227
+
1228
+
1229
+ /* DATEPICKER DATA-API
1230
+ * ================== */
1231
+
1232
+ $(document).on(
1233
+ 'focus.datepicker.data-api click.datepicker.data-api',
1234
+ '[data-provide="datepicker"]',
1235
+ function(e){
1236
+ var $this = $(this);
1237
+ if ($this.data('datepicker')) return;
1238
+ e.preventDefault();
1239
+ // component click requires us to explicitly show it
1240
+ $this.datepicker('show');
1241
+ }
1242
+ );
1243
+ $(function(){
1244
+ $('[data-provide="datepicker-inline"]').datepicker();
1245
+ });
1246
+
1025
1247
  }( window.jQuery );