bootstrap-rails-engine 1.3.0.3 → 1.3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 );