bootstrap-daterangepicker-rails 0.1.1 → 0.1.8

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.
@@ -1,10 +1,9 @@
1
1
  /**
2
- * @version: 1.3.17
2
+ * @version: 2.1.19
3
3
  * @author: Dan Grossman http://www.dangrossman.info/
4
- * @date: 2014-11-25
5
- * @copyright: Copyright (c) 2012-2014 Dan Grossman. All rights reserved.
4
+ * @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved.
6
5
  * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
7
- * @website: http://www.improvely.com/
6
+ * @website: https://www.improvely.com/
8
7
  */
9
8
 
10
9
  (function(root, factory) {
@@ -15,71 +14,381 @@
15
14
  });
16
15
 
17
16
  } else if (typeof exports !== 'undefined') {
18
- var momentjs = require('moment');
19
- var jQuery;
20
- try {
21
- jQuery = require('jquery');
22
- } catch (err) {
23
- jQuery = window.jQuery;
24
- if (!jQuery) throw new Error('jQuery dependency not found');
25
- }
17
+ var momentjs = require('moment');
18
+ var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; //isomorphic issue
19
+ if (!jQuery) {
20
+ try {
21
+ jQuery = require('jquery');
22
+ if (!jQuery.fn) jQuery.fn = {}; //isomorphic issue
23
+ } catch (err) {
24
+ if (!jQuery) throw new Error('jQuery dependency not found');
25
+ }
26
+ }
26
27
 
27
28
  factory(root, exports, momentjs, jQuery);
28
29
 
29
30
  // Finally, as a browser global.
30
31
  } else {
31
- root.daterangepicker = factory(root, {}, root.moment, (root.jQuery || root.Zepto || root.ender || root.$));
32
+ root.daterangepicker = factory(root, {}, root.moment || moment, (root.jQuery || root.Zepto || root.ender || root.$));
32
33
  }
33
34
 
34
- }(this, function(root, daterangepicker, moment, $) {
35
+ }(this || {}, function(root, daterangepicker, moment, $) { // 'this' doesn't exist on a server
35
36
 
36
- var DateRangePicker = function (element, options, cb) {
37
+ var DateRangePicker = function(element, options, cb) {
37
38
 
38
- // by default, the daterangepicker element is placed at the bottom of HTML body
39
+ //default settings for options
39
40
  this.parentEl = 'body';
40
-
41
- //element that triggered the date range picker
42
41
  this.element = $(element);
43
-
44
- //tracks visible state
42
+ this.startDate = moment().startOf('day');
43
+ this.endDate = moment().endOf('day');
44
+ this.minDate = false;
45
+ this.maxDate = false;
46
+ this.dateLimit = false;
47
+ this.autoApply = false;
48
+ this.singleDatePicker = false;
49
+ this.showDropdowns = false;
50
+ this.showWeekNumbers = false;
51
+ this.showISOWeekNumbers = false;
52
+ this.timePicker = false;
53
+ this.timePicker24Hour = false;
54
+ this.timePickerIncrement = 1;
55
+ this.timePickerSeconds = false;
56
+ this.linkedCalendars = true;
57
+ this.autoUpdateInput = true;
58
+ this.alwaysShowCalendars = false;
59
+ this.ranges = {};
60
+
61
+ this.opens = 'right';
62
+ if (this.element.hasClass('pull-right'))
63
+ this.opens = 'left';
64
+
65
+ this.drops = 'down';
66
+ if (this.element.hasClass('dropup'))
67
+ this.drops = 'up';
68
+
69
+ this.buttonClasses = 'btn btn-sm';
70
+ this.applyClass = 'btn-success';
71
+ this.cancelClass = 'btn-default';
72
+
73
+ this.locale = {
74
+ format: 'MM/DD/YYYY',
75
+ separator: ' - ',
76
+ applyLabel: 'Apply',
77
+ cancelLabel: 'Cancel',
78
+ weekLabel: 'W',
79
+ customRangeLabel: 'Custom Range',
80
+ daysOfWeek: moment.weekdaysMin(),
81
+ monthNames: moment.monthsShort(),
82
+ firstDay: moment.localeData().firstDayOfWeek()
83
+ };
84
+
85
+ this.callback = function() { };
86
+
87
+ //some state information
45
88
  this.isShowing = false;
89
+ this.leftCalendar = {};
90
+ this.rightCalendar = {};
46
91
 
47
- //create the picker HTML object
48
- var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
49
- '<div class="calendar first left"></div>' +
50
- '<div class="calendar second right"></div>' +
51
- '<div class="ranges">' +
52
- '<div class="range_inputs">' +
53
- '<div class="daterangepicker_start_input">' +
54
- '<label for="daterangepicker_start"></label>' +
92
+ //custom options from user
93
+ if (typeof options !== 'object' || options === null)
94
+ options = {};
95
+
96
+ //allow setting options with data attributes
97
+ //data-api options will be overwritten with custom javascript options
98
+ options = $.extend(this.element.data(), options);
99
+
100
+ //html template for the picker UI
101
+ if (typeof options.template !== 'string' && !(options.template instanceof $))
102
+ options.template = '<div class="daterangepicker dropdown-menu">' +
103
+ '<div class="calendar left">' +
104
+ '<div class="daterangepicker_input">' +
55
105
  '<input class="input-mini" type="text" name="daterangepicker_start" value="" />' +
106
+ '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
107
+ '<div class="calendar-time">' +
108
+ '<div></div>' +
109
+ '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
110
+ '</div>' +
56
111
  '</div>' +
57
- '<div class="daterangepicker_end_input">' +
58
- '<label for="daterangepicker_end"></label>' +
112
+ '<div class="calendar-table"></div>' +
113
+ '</div>' +
114
+ '<div class="calendar right">' +
115
+ '<div class="daterangepicker_input">' +
59
116
  '<input class="input-mini" type="text" name="daterangepicker_end" value="" />' +
117
+ '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
118
+ '<div class="calendar-time">' +
119
+ '<div></div>' +
120
+ '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
121
+ '</div>' +
122
+ '</div>' +
123
+ '<div class="calendar-table"></div>' +
124
+ '</div>' +
125
+ '<div class="ranges">' +
126
+ '<div class="range_inputs">' +
127
+ '<button class="applyBtn" disabled="disabled" type="button"></button> ' +
128
+ '<button class="cancelBtn" type="button"></button>' +
60
129
  '</div>' +
61
- '<button class="applyBtn" disabled="disabled"></button>&nbsp;' +
62
- '<button class="cancelBtn"></button>' +
63
- '</div>' +
64
130
  '</div>' +
65
- '</div>';
131
+ '</div>';
66
132
 
67
- //custom options
68
- if (typeof options !== 'object' || options === null)
69
- options = {};
133
+ this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
134
+ this.container = $(options.template).appendTo(this.parentEl);
135
+
136
+ //
137
+ // handle all the possible options overriding defaults
138
+ //
139
+
140
+ if (typeof options.locale === 'object') {
141
+
142
+ if (typeof options.locale.format === 'string')
143
+ this.locale.format = options.locale.format;
144
+
145
+ if (typeof options.locale.separator === 'string')
146
+ this.locale.separator = options.locale.separator;
147
+
148
+ if (typeof options.locale.daysOfWeek === 'object')
149
+ this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
150
+
151
+ if (typeof options.locale.monthNames === 'object')
152
+ this.locale.monthNames = options.locale.monthNames.slice();
153
+
154
+ if (typeof options.locale.firstDay === 'number')
155
+ this.locale.firstDay = options.locale.firstDay;
156
+
157
+ if (typeof options.locale.applyLabel === 'string')
158
+ this.locale.applyLabel = options.locale.applyLabel;
159
+
160
+ if (typeof options.locale.cancelLabel === 'string')
161
+ this.locale.cancelLabel = options.locale.cancelLabel;
162
+
163
+ if (typeof options.locale.weekLabel === 'string')
164
+ this.locale.weekLabel = options.locale.weekLabel;
165
+
166
+ if (typeof options.locale.customRangeLabel === 'string')
167
+ this.locale.customRangeLabel = options.locale.customRangeLabel;
168
+
169
+ }
170
+
171
+ if (typeof options.startDate === 'string')
172
+ this.startDate = moment(options.startDate, this.locale.format);
173
+
174
+ if (typeof options.endDate === 'string')
175
+ this.endDate = moment(options.endDate, this.locale.format);
176
+
177
+ if (typeof options.minDate === 'string')
178
+ this.minDate = moment(options.minDate, this.locale.format);
179
+
180
+ if (typeof options.maxDate === 'string')
181
+ this.maxDate = moment(options.maxDate, this.locale.format);
182
+
183
+ if (typeof options.startDate === 'object')
184
+ this.startDate = moment(options.startDate);
185
+
186
+ if (typeof options.endDate === 'object')
187
+ this.endDate = moment(options.endDate);
188
+
189
+ if (typeof options.minDate === 'object')
190
+ this.minDate = moment(options.minDate);
191
+
192
+ if (typeof options.maxDate === 'object')
193
+ this.maxDate = moment(options.maxDate);
194
+
195
+ // sanity check for bad options
196
+ if (this.minDate && this.startDate.isBefore(this.minDate))
197
+ this.startDate = this.minDate.clone();
198
+
199
+ // sanity check for bad options
200
+ if (this.maxDate && this.endDate.isAfter(this.maxDate))
201
+ this.endDate = this.maxDate.clone();
202
+
203
+ if (typeof options.applyClass === 'string')
204
+ this.applyClass = options.applyClass;
205
+
206
+ if (typeof options.cancelClass === 'string')
207
+ this.cancelClass = options.cancelClass;
208
+
209
+ if (typeof options.dateLimit === 'object')
210
+ this.dateLimit = options.dateLimit;
211
+
212
+ if (typeof options.opens === 'string')
213
+ this.opens = options.opens;
214
+
215
+ if (typeof options.drops === 'string')
216
+ this.drops = options.drops;
217
+
218
+ if (typeof options.showWeekNumbers === 'boolean')
219
+ this.showWeekNumbers = options.showWeekNumbers;
220
+
221
+ if (typeof options.showISOWeekNumbers === 'boolean')
222
+ this.showISOWeekNumbers = options.showISOWeekNumbers;
223
+
224
+ if (typeof options.buttonClasses === 'string')
225
+ this.buttonClasses = options.buttonClasses;
70
226
 
71
- this.parentEl = (typeof options === 'object' && options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
72
- this.container = $(DRPTemplate).appendTo(this.parentEl);
227
+ if (typeof options.buttonClasses === 'object')
228
+ this.buttonClasses = options.buttonClasses.join(' ');
73
229
 
74
- this.setOptions(options, cb);
230
+ if (typeof options.showDropdowns === 'boolean')
231
+ this.showDropdowns = options.showDropdowns;
232
+
233
+ if (typeof options.singleDatePicker === 'boolean') {
234
+ this.singleDatePicker = options.singleDatePicker;
235
+ if (this.singleDatePicker)
236
+ this.endDate = this.startDate.clone();
237
+ }
238
+
239
+ if (typeof options.timePicker === 'boolean')
240
+ this.timePicker = options.timePicker;
241
+
242
+ if (typeof options.timePickerSeconds === 'boolean')
243
+ this.timePickerSeconds = options.timePickerSeconds;
244
+
245
+ if (typeof options.timePickerIncrement === 'number')
246
+ this.timePickerIncrement = options.timePickerIncrement;
247
+
248
+ if (typeof options.timePicker24Hour === 'boolean')
249
+ this.timePicker24Hour = options.timePicker24Hour;
250
+
251
+ if (typeof options.autoApply === 'boolean')
252
+ this.autoApply = options.autoApply;
253
+
254
+ if (typeof options.autoUpdateInput === 'boolean')
255
+ this.autoUpdateInput = options.autoUpdateInput;
256
+
257
+ if (typeof options.linkedCalendars === 'boolean')
258
+ this.linkedCalendars = options.linkedCalendars;
259
+
260
+ if (typeof options.isInvalidDate === 'function')
261
+ this.isInvalidDate = options.isInvalidDate;
262
+
263
+ if (typeof options.alwaysShowCalendars === 'boolean')
264
+ this.alwaysShowCalendars = options.alwaysShowCalendars;
265
+
266
+ // update day names order to firstDay
267
+ if (this.locale.firstDay != 0) {
268
+ var iterator = this.locale.firstDay;
269
+ while (iterator > 0) {
270
+ this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
271
+ iterator--;
272
+ }
273
+ }
274
+
275
+ var start, end, range;
276
+
277
+ //if no start/end dates set, check if an input element contains initial values
278
+ if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
279
+ if ($(this.element).is('input[type=text]')) {
280
+ var val = $(this.element).val(),
281
+ split = val.split(this.locale.separator);
282
+
283
+ start = end = null;
284
+
285
+ if (split.length == 2) {
286
+ start = moment(split[0], this.locale.format);
287
+ end = moment(split[1], this.locale.format);
288
+ } else if (this.singleDatePicker && val !== "") {
289
+ start = moment(val, this.locale.format);
290
+ end = moment(val, this.locale.format);
291
+ }
292
+ if (start !== null && end !== null) {
293
+ this.setStartDate(start);
294
+ this.setEndDate(end);
295
+ }
296
+ }
297
+ }
298
+
299
+ if (typeof options.ranges === 'object') {
300
+ for (range in options.ranges) {
301
+
302
+ if (typeof options.ranges[range][0] === 'string')
303
+ start = moment(options.ranges[range][0], this.locale.format);
304
+ else
305
+ start = moment(options.ranges[range][0]);
306
+
307
+ if (typeof options.ranges[range][1] === 'string')
308
+ end = moment(options.ranges[range][1], this.locale.format);
309
+ else
310
+ end = moment(options.ranges[range][1]);
311
+
312
+ // If the start or end date exceed those allowed by the minDate or dateLimit
313
+ // options, shorten the range to the allowable period.
314
+ if (this.minDate && start.isBefore(this.minDate))
315
+ start = this.minDate.clone();
316
+
317
+ var maxDate = this.maxDate;
318
+ if (this.dateLimit && start.clone().add(this.dateLimit).isAfter(maxDate))
319
+ maxDate = start.clone().add(this.dateLimit);
320
+ if (maxDate && end.isAfter(maxDate))
321
+ end = maxDate.clone();
322
+
323
+ // If the end of the range is before the minimum or the start of the range is
324
+ // after the maximum, don't display this range option at all.
325
+ if ((this.minDate && end.isBefore(this.minDate)) || (maxDate && start.isAfter(maxDate)))
326
+ continue;
327
+
328
+ //Support unicode chars in the range names.
329
+ var elem = document.createElement('textarea');
330
+ elem.innerHTML = range;
331
+ var rangeHtml = elem.value;
332
+
333
+ this.ranges[rangeHtml] = [start, end];
334
+ }
335
+
336
+ var list = '<ul>';
337
+ for (range in this.ranges) {
338
+ list += '<li>' + range + '</li>';
339
+ }
340
+ list += '<li>' + this.locale.customRangeLabel + '</li>';
341
+ list += '</ul>';
342
+ this.container.find('.ranges').prepend(list);
343
+ }
344
+
345
+ if (typeof cb === 'function') {
346
+ this.callback = cb;
347
+ }
348
+
349
+ if (!this.timePicker) {
350
+ this.startDate = this.startDate.startOf('day');
351
+ this.endDate = this.endDate.endOf('day');
352
+ this.container.find('.calendar-time').hide();
353
+ }
354
+
355
+ //can't be used together for now
356
+ if (this.timePicker && this.autoApply)
357
+ this.autoApply = false;
358
+
359
+ if (this.autoApply && typeof options.ranges !== 'object') {
360
+ this.container.find('.ranges').hide();
361
+ } else if (this.autoApply) {
362
+ this.container.find('.applyBtn, .cancelBtn').addClass('hide');
363
+ }
364
+
365
+ if (this.singleDatePicker) {
366
+ this.container.addClass('single');
367
+ this.container.find('.calendar.left').addClass('single');
368
+ this.container.find('.calendar.left').show();
369
+ this.container.find('.calendar.right').hide();
370
+ this.container.find('.daterangepicker_input input, .daterangepicker_input i').hide();
371
+ if (!this.timePicker) {
372
+ this.container.find('.ranges').hide();
373
+ }
374
+ }
375
+
376
+ if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) {
377
+ this.container.addClass('show-calendar');
378
+ }
379
+
380
+ this.container.addClass('opens' + this.opens);
381
+
382
+ //swap the position of the predefined ranges if opens right
383
+ if (typeof options.ranges !== 'undefined' && this.opens == 'right') {
384
+ var ranges = this.container.find('.ranges');
385
+ var html = ranges.clone();
386
+ ranges.remove();
387
+ this.container.find('.calendar.left').parent().prepend(html);
388
+ }
75
389
 
76
390
  //apply CSS classes and labels to buttons
77
- var c = this.container;
78
- $.each(this.buttonClasses, function (idx, val) {
79
- c.find('button').addClass(val);
80
- });
81
- this.container.find('.daterangepicker_start_input label').html(this.locale.fromLabel);
82
- this.container.find('.daterangepicker_end_input label').html(this.locale.toLabel);
391
+ this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses);
83
392
  if (this.applyClass.length)
84
393
  this.container.find('.applyBtn').addClass(this.applyClass);
85
394
  if (this.cancelClass.length)
@@ -87,7 +396,9 @@
87
396
  this.container.find('.applyBtn').html(this.locale.applyLabel);
88
397
  this.container.find('.cancelBtn').html(this.locale.cancelLabel);
89
398
 
90
- //event listeners
399
+ //
400
+ // event listeners
401
+ //
91
402
 
92
403
  this.container.find('.calendar')
93
404
  .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
@@ -95,446 +406,599 @@
95
406
  .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
96
407
  .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
97
408
  .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
98
- .on('change.daterangepicker', 'select.yearselect', $.proxy(this.updateMonthYear, this))
99
- .on('change.daterangepicker', 'select.monthselect', $.proxy(this.updateMonthYear, this))
100
- .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.updateTime, this));
409
+ .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))
410
+ .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
411
+ .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))
412
+ .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this))
413
+ //.on('keyup.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))
414
+ .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this));
101
415
 
102
416
  this.container.find('.ranges')
103
417
  .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
104
418
  .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
105
- .on('click.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
106
- .on('change.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsChanged, this))
107
- .on('keydown.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsKeydown, this))
108
419
  .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
109
- .on('mouseenter.daterangepicker', 'li', $.proxy(this.enterRange, this))
420
+ .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this))
110
421
  .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
111
422
 
112
423
  if (this.element.is('input')) {
113
424
  this.element.on({
114
425
  'click.daterangepicker': $.proxy(this.show, this),
115
426
  'focus.daterangepicker': $.proxy(this.show, this),
116
- 'keyup.daterangepicker': $.proxy(this.updateFromControl, this)
427
+ 'keyup.daterangepicker': $.proxy(this.elementChanged, this),
428
+ 'keydown.daterangepicker': $.proxy(this.keydown, this)
117
429
  });
118
430
  } else {
119
431
  this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
120
432
  }
121
433
 
434
+ //
435
+ // if attached to a text input, set the initial value
436
+ //
437
+
438
+ if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
439
+ this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
440
+ this.element.trigger('change');
441
+ } else if (this.element.is('input') && this.autoUpdateInput) {
442
+ this.element.val(this.startDate.format(this.locale.format));
443
+ this.element.trigger('change');
444
+ }
445
+
122
446
  };
123
447
 
124
448
  DateRangePicker.prototype = {
125
449
 
126
450
  constructor: DateRangePicker,
127
451
 
128
- setOptions: function(options, callback) {
129
-
130
- this.startDate = moment().startOf('day');
131
- this.endDate = moment().endOf('day');
132
- this.timeZone = moment().zone();
133
- this.minDate = false;
134
- this.maxDate = false;
135
- this.dateLimit = false;
452
+ setStartDate: function(startDate) {
453
+ if (typeof startDate === 'string')
454
+ this.startDate = moment(startDate, this.locale.format);
136
455
 
137
- this.showDropdowns = false;
138
- this.showWeekNumbers = false;
139
- this.timePicker = false;
140
- this.timePickerSeconds = false;
141
- this.timePickerIncrement = 30;
142
- this.timePicker12Hour = true;
143
- this.singleDatePicker = false;
144
- this.ranges = {};
456
+ if (typeof startDate === 'object')
457
+ this.startDate = moment(startDate);
145
458
 
146
- this.opens = 'right';
147
- if (this.element.hasClass('pull-right'))
148
- this.opens = 'left';
459
+ if (!this.timePicker)
460
+ this.startDate = this.startDate.startOf('day');
149
461
 
150
- this.buttonClasses = ['btn', 'btn-small btn-sm'];
151
- this.applyClass = 'btn-success';
152
- this.cancelClass = 'btn-default';
462
+ if (this.timePicker && this.timePickerIncrement)
463
+ this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
153
464
 
154
- this.format = 'MM/DD/YYYY';
155
- this.separator = ' - ';
465
+ if (this.minDate && this.startDate.isBefore(this.minDate))
466
+ this.startDate = this.minDate;
156
467
 
157
- this.locale = {
158
- applyLabel: 'Apply',
159
- cancelLabel: 'Cancel',
160
- fromLabel: 'From',
161
- toLabel: 'To',
162
- weekLabel: 'W',
163
- customRangeLabel: 'Custom Range',
164
- daysOfWeek: moment.weekdaysMin(),
165
- monthNames: moment.monthsShort(),
166
- firstDay: moment.localeData()._week.dow
167
- };
468
+ if (this.maxDate && this.startDate.isAfter(this.maxDate))
469
+ this.startDate = this.maxDate;
168
470
 
169
- this.cb = function () { };
471
+ if (!this.isShowing)
472
+ this.updateElement();
170
473
 
171
- if (typeof options.format === 'string')
172
- this.format = options.format;
474
+ this.updateMonthsInView();
475
+ },
173
476
 
174
- if (typeof options.separator === 'string')
175
- this.separator = options.separator;
477
+ setEndDate: function(endDate) {
478
+ if (typeof endDate === 'string')
479
+ this.endDate = moment(endDate, this.locale.format);
176
480
 
177
- if (typeof options.startDate === 'string')
178
- this.startDate = moment(options.startDate, this.format);
481
+ if (typeof endDate === 'object')
482
+ this.endDate = moment(endDate);
179
483
 
180
- if (typeof options.endDate === 'string')
181
- this.endDate = moment(options.endDate, this.format);
484
+ if (!this.timePicker)
485
+ this.endDate = this.endDate.endOf('day');
182
486
 
183
- if (typeof options.minDate === 'string')
184
- this.minDate = moment(options.minDate, this.format);
487
+ if (this.timePicker && this.timePickerIncrement)
488
+ this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
185
489
 
186
- if (typeof options.maxDate === 'string')
187
- this.maxDate = moment(options.maxDate, this.format);
490
+ if (this.endDate.isBefore(this.startDate))
491
+ this.endDate = this.startDate.clone();
188
492
 
189
- if (typeof options.startDate === 'object')
190
- this.startDate = moment(options.startDate);
493
+ if (this.maxDate && this.endDate.isAfter(this.maxDate))
494
+ this.endDate = this.maxDate;
191
495
 
192
- if (typeof options.endDate === 'object')
193
- this.endDate = moment(options.endDate);
496
+ if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate))
497
+ this.endDate = this.startDate.clone().add(this.dateLimit);
194
498
 
195
- if (typeof options.minDate === 'object')
196
- this.minDate = moment(options.minDate);
499
+ this.previousRightTime = this.endDate.clone();
197
500
 
198
- if (typeof options.maxDate === 'object')
199
- this.maxDate = moment(options.maxDate);
501
+ if (!this.isShowing)
502
+ this.updateElement();
200
503
 
201
- if (typeof options.applyClass === 'string')
202
- this.applyClass = options.applyClass;
504
+ this.updateMonthsInView();
505
+ },
203
506
 
204
- if (typeof options.cancelClass === 'string')
205
- this.cancelClass = options.cancelClass;
507
+ isInvalidDate: function() {
508
+ return false;
509
+ },
206
510
 
207
- if (typeof options.dateLimit === 'object')
208
- this.dateLimit = options.dateLimit;
511
+ updateView: function() {
512
+ if (this.timePicker) {
513
+ this.renderTimePicker('left');
514
+ this.renderTimePicker('right');
515
+ if (!this.endDate) {
516
+ this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled');
517
+ } else {
518
+ this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled');
519
+ }
520
+ }
521
+ if (this.endDate) {
522
+ this.container.find('input[name="daterangepicker_end"]').removeClass('active');
523
+ this.container.find('input[name="daterangepicker_start"]').addClass('active');
524
+ } else {
525
+ this.container.find('input[name="daterangepicker_end"]').addClass('active');
526
+ this.container.find('input[name="daterangepicker_start"]').removeClass('active');
527
+ }
528
+ this.updateMonthsInView();
529
+ this.updateCalendars();
530
+ this.updateFormInputs();
531
+ },
209
532
 
210
- if (typeof options.locale === 'object') {
533
+ updateMonthsInView: function() {
534
+ if (this.endDate) {
211
535
 
212
- if (typeof options.locale.daysOfWeek === 'object') {
213
- // Create a copy of daysOfWeek to avoid modification of original
214
- // options object for reusability in multiple daterangepicker instances
215
- this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
536
+ //if both dates are visible already, do nothing
537
+ if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month &&
538
+ (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
539
+ &&
540
+ (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
541
+ ) {
542
+ return;
216
543
  }
217
544
 
218
- if (typeof options.locale.monthNames === 'object') {
219
- this.locale.monthNames = options.locale.monthNames.slice();
545
+ this.leftCalendar.month = this.startDate.clone().date(2);
546
+ if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) {
547
+ this.rightCalendar.month = this.endDate.clone().date(2);
548
+ } else {
549
+ this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
220
550
  }
221
551
 
222
- if (typeof options.locale.firstDay === 'number') {
223
- this.locale.firstDay = options.locale.firstDay;
552
+ } else {
553
+ if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) {
554
+ this.leftCalendar.month = this.startDate.clone().date(2);
555
+ this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
224
556
  }
557
+ }
558
+ },
225
559
 
226
- if (typeof options.locale.applyLabel === 'string') {
227
- this.locale.applyLabel = options.locale.applyLabel;
228
- }
560
+ updateCalendars: function() {
229
561
 
230
- if (typeof options.locale.cancelLabel === 'string') {
231
- this.locale.cancelLabel = options.locale.cancelLabel;
562
+ if (this.timePicker) {
563
+ var hour, minute, second;
564
+ if (this.endDate) {
565
+ hour = parseInt(this.container.find('.left .hourselect').val(), 10);
566
+ minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
567
+ second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
568
+ if (!this.timePicker24Hour) {
569
+ var ampm = this.container.find('.left .ampmselect').val();
570
+ if (ampm === 'PM' && hour < 12)
571
+ hour += 12;
572
+ if (ampm === 'AM' && hour === 12)
573
+ hour = 0;
574
+ }
575
+ } else {
576
+ hour = parseInt(this.container.find('.right .hourselect').val(), 10);
577
+ minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
578
+ second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
579
+ if (!this.timePicker24Hour) {
580
+ var ampm = this.container.find('.right .ampmselect').val();
581
+ if (ampm === 'PM' && hour < 12)
582
+ hour += 12;
583
+ if (ampm === 'AM' && hour === 12)
584
+ hour = 0;
585
+ }
232
586
  }
587
+ this.leftCalendar.month.hour(hour).minute(minute).second(second);
588
+ this.rightCalendar.month.hour(hour).minute(minute).second(second);
589
+ }
233
590
 
234
- if (typeof options.locale.fromLabel === 'string') {
235
- this.locale.fromLabel = options.locale.fromLabel;
236
- }
591
+ this.renderCalendar('left');
592
+ this.renderCalendar('right');
237
593
 
238
- if (typeof options.locale.toLabel === 'string') {
239
- this.locale.toLabel = options.locale.toLabel;
240
- }
594
+ //highlight any predefined range matching the current start and end dates
595
+ this.container.find('.ranges li').removeClass('active');
596
+ if (this.endDate == null) return;
241
597
 
242
- if (typeof options.locale.weekLabel === 'string') {
243
- this.locale.weekLabel = options.locale.weekLabel;
244
- }
598
+ this.calculateChosenLabel();
599
+ },
245
600
 
246
- if (typeof options.locale.customRangeLabel === 'string') {
247
- this.locale.customRangeLabel = options.locale.customRangeLabel;
248
- }
249
- }
601
+ renderCalendar: function(side) {
250
602
 
251
- if (typeof options.opens === 'string')
252
- this.opens = options.opens;
603
+ //
604
+ // Build the matrix of dates that will populate the calendar
605
+ //
253
606
 
254
- if (typeof options.showWeekNumbers === 'boolean') {
255
- this.showWeekNumbers = options.showWeekNumbers;
256
- }
607
+ var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar;
608
+ var month = calendar.month.month();
609
+ var year = calendar.month.year();
610
+ var hour = calendar.month.hour();
611
+ var minute = calendar.month.minute();
612
+ var second = calendar.month.second();
613
+ var daysInMonth = moment([year, month]).daysInMonth();
614
+ var firstDay = moment([year, month, 1]);
615
+ var lastDay = moment([year, month, daysInMonth]);
616
+ var lastMonth = moment(firstDay).subtract(1, 'month').month();
617
+ var lastYear = moment(firstDay).subtract(1, 'month').year();
618
+ var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
619
+ var dayOfWeek = firstDay.day();
257
620
 
258
- if (typeof options.buttonClasses === 'string') {
259
- this.buttonClasses = [options.buttonClasses];
260
- }
621
+ //initialize a 6 rows x 7 columns array for the calendar
622
+ var calendar = [];
623
+ calendar.firstDay = firstDay;
624
+ calendar.lastDay = lastDay;
261
625
 
262
- if (typeof options.buttonClasses === 'object') {
263
- this.buttonClasses = options.buttonClasses;
626
+ for (var i = 0; i < 6; i++) {
627
+ calendar[i] = [];
264
628
  }
265
629
 
266
- if (typeof options.showDropdowns === 'boolean') {
267
- this.showDropdowns = options.showDropdowns;
268
- }
630
+ //populate the calendar with date objects
631
+ var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
632
+ if (startDay > daysInLastMonth)
633
+ startDay -= 7;
269
634
 
270
- if (typeof options.singleDatePicker === 'boolean') {
271
- this.singleDatePicker = options.singleDatePicker;
272
- if (this.singleDatePicker) {
273
- this.endDate = this.startDate.clone();
635
+ if (dayOfWeek == this.locale.firstDay)
636
+ startDay = daysInLastMonth - 6;
637
+
638
+ var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]);
639
+
640
+ var col, row;
641
+ for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
642
+ if (i > 0 && col % 7 === 0) {
643
+ col = 0;
644
+ row++;
274
645
  }
275
- }
646
+ calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second);
647
+ curDate.hour(12);
276
648
 
277
- if (typeof options.timePicker === 'boolean') {
278
- this.timePicker = options.timePicker;
279
- }
649
+ if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
650
+ calendar[row][col] = this.minDate.clone();
651
+ }
280
652
 
281
- if (typeof options.timePickerSeconds === 'boolean') {
282
- this.timePickerSeconds = options.timePickerSeconds;
283
- }
653
+ if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
654
+ calendar[row][col] = this.maxDate.clone();
655
+ }
284
656
 
285
- if (typeof options.timePickerIncrement === 'number') {
286
- this.timePickerIncrement = options.timePickerIncrement;
287
657
  }
288
658
 
289
- if (typeof options.timePicker12Hour === 'boolean') {
290
- this.timePicker12Hour = options.timePicker12Hour;
659
+ //make the calendar object available to hoverDate/clickDate
660
+ if (side == 'left') {
661
+ this.leftCalendar.calendar = calendar;
662
+ } else {
663
+ this.rightCalendar.calendar = calendar;
291
664
  }
292
665
 
293
- // update day names order to firstDay
294
- if (this.locale.firstDay != 0) {
295
- var iterator = this.locale.firstDay;
296
- while (iterator > 0) {
297
- this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
298
- iterator--;
299
- }
300
- }
666
+ //
667
+ // Display the calendar
668
+ //
301
669
 
302
- var start, end, range;
303
-
304
- //if no start/end dates set, check if an input element contains initial values
305
- if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
306
- if ($(this.element).is('input[type=text]')) {
307
- var val = $(this.element).val(),
308
- split = val.split(this.separator);
309
-
310
- start = end = null;
311
-
312
- if (split.length == 2) {
313
- start = moment(split[0], this.format);
314
- end = moment(split[1], this.format);
315
- } else if (this.singleDatePicker && val !== "") {
316
- start = moment(val, this.format);
317
- end = moment(val, this.format);
318
- }
319
- if (start !== null && end !== null) {
320
- this.startDate = start;
321
- this.endDate = end;
322
- }
323
- }
324
- }
670
+ var minDate = side == 'left' ? this.minDate : this.startDate;
671
+ var maxDate = this.maxDate;
672
+ var selected = side == 'left' ? this.startDate : this.endDate;
673
+
674
+ var html = '<table class="table-condensed">';
675
+ html += '<thead>';
676
+ html += '<tr>';
677
+
678
+ // add empty cell for week number
679
+ if (this.showWeekNumbers || this.showISOWeekNumbers)
680
+ html += '<th></th>';
325
681
 
326
- // bind the time zone used to build the calendar to either the timeZone passed in through the options or the zone of the startDate (which will be the local time zone by default)
327
- if (typeof options.timeZone === 'string' || typeof options.timeZone === 'number') {
328
- this.timeZone = options.timeZone;
329
- this.startDate.zone(this.timeZone);
330
- this.endDate.zone(this.timeZone);
682
+ if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {
683
+ html += '<th class="prev available"><i class="fa fa-chevron-left glyphicon glyphicon-chevron-left"></i></th>';
331
684
  } else {
332
- this.timeZone = moment(this.startDate).zone();
685
+ html += '<th></th>';
333
686
  }
334
687
 
335
- if (typeof options.ranges === 'object') {
336
- for (range in options.ranges) {
337
-
338
- if (typeof options.ranges[range][0] === 'string')
339
- start = moment(options.ranges[range][0], this.format);
340
- else
341
- start = moment(options.ranges[range][0]);
342
-
343
- if (typeof options.ranges[range][1] === 'string')
344
- end = moment(options.ranges[range][1], this.format);
345
- else
346
- end = moment(options.ranges[range][1]);
347
-
348
- // If we have a min/max date set, bound this range
349
- // to it, but only if it would otherwise fall
350
- // outside of the min/max.
351
- if (this.minDate && start.isBefore(this.minDate))
352
- start = moment(this.minDate);
353
-
354
- if (this.maxDate && end.isAfter(this.maxDate))
355
- end = moment(this.maxDate);
356
-
357
- // If the end of the range is before the minimum (if min is set) OR
358
- // the start of the range is after the max (also if set) don't display this
359
- // range option.
360
- if ((this.minDate && end.isBefore(this.minDate)) || (this.maxDate && start.isAfter(this.maxDate))) {
361
- continue;
362
- }
688
+ var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
363
689
 
364
- this.ranges[range] = [start, end];
690
+ if (this.showDropdowns) {
691
+ var currentMonth = calendar[1][1].month();
692
+ var currentYear = calendar[1][1].year();
693
+ var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
694
+ var minYear = (minDate && minDate.year()) || (currentYear - 50);
695
+ var inMinYear = currentYear == minYear;
696
+ var inMaxYear = currentYear == maxYear;
697
+
698
+ var monthHtml = '<select class="monthselect">';
699
+ for (var m = 0; m < 12; m++) {
700
+ if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
701
+ monthHtml += "<option value='" + m + "'" +
702
+ (m === currentMonth ? " selected='selected'" : "") +
703
+ ">" + this.locale.monthNames[m] + "</option>";
704
+ } else {
705
+ monthHtml += "<option value='" + m + "'" +
706
+ (m === currentMonth ? " selected='selected'" : "") +
707
+ " disabled='disabled'>" + this.locale.monthNames[m] + "</option>";
708
+ }
365
709
  }
710
+ monthHtml += "</select>";
366
711
 
367
- var list = '<ul>';
368
- for (range in this.ranges) {
369
- list += '<li>' + range + '</li>';
712
+ var yearHtml = '<select class="yearselect">';
713
+ for (var y = minYear; y <= maxYear; y++) {
714
+ yearHtml += '<option value="' + y + '"' +
715
+ (y === currentYear ? ' selected="selected"' : '') +
716
+ '>' + y + '</option>';
370
717
  }
371
- list += '<li>' + this.locale.customRangeLabel + '</li>';
372
- list += '</ul>';
373
- this.container.find('.ranges ul').remove();
374
- this.container.find('.ranges').prepend(list);
375
- }
376
-
377
- if (typeof callback === 'function') {
378
- this.cb = callback;
379
- }
718
+ yearHtml += '</select>';
380
719
 
381
- if (!this.timePicker) {
382
- this.startDate = this.startDate.startOf('day');
383
- this.endDate = this.endDate.endOf('day');
720
+ dateHtml = monthHtml + yearHtml;
384
721
  }
385
722
 
386
- if (this.singleDatePicker) {
387
- this.opens = 'right';
388
- this.container.addClass('single');
389
- this.container.find('.calendar.right').show();
390
- this.container.find('.calendar.left').hide();
391
- if (!this.timePicker) {
392
- this.container.find('.ranges').hide();
393
- } else {
394
- this.container.find('.ranges .daterangepicker_start_input, .ranges .daterangepicker_end_input').hide();
395
- }
396
- if (!this.container.find('.calendar.right').hasClass('single'))
397
- this.container.find('.calendar.right').addClass('single');
723
+ html += '<th colspan="5" class="month">' + dateHtml + '</th>';
724
+ if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {
725
+ html += '<th class="next available"><i class="fa fa-chevron-right glyphicon glyphicon-chevron-right"></i></th>';
398
726
  } else {
399
- this.container.removeClass('single');
400
- this.container.find('.calendar.right').removeClass('single');
401
- this.container.find('.ranges').show();
727
+ html += '<th></th>';
402
728
  }
403
729
 
404
- this.oldStartDate = this.startDate.clone();
405
- this.oldEndDate = this.endDate.clone();
406
- this.oldChosenLabel = this.chosenLabel;
407
-
408
- this.leftCalendar = {
409
- month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute(), this.startDate.second()]),
410
- calendar: []
411
- };
412
-
413
- this.rightCalendar = {
414
- month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute(), this.endDate.second()]),
415
- calendar: []
416
- };
417
-
418
- if (this.opens == 'right' || this.opens == 'center') {
419
- //swap calendar positions
420
- var first = this.container.find('.calendar.first');
421
- var second = this.container.find('.calendar.second');
422
-
423
- if (second.hasClass('single')) {
424
- second.removeClass('single');
425
- first.addClass('single');
426
- }
730
+ html += '</tr>';
731
+ html += '<tr>';
427
732
 
428
- first.removeClass('left').addClass('right');
429
- second.removeClass('right').addClass('left');
733
+ // add week number label
734
+ if (this.showWeekNumbers || this.showISOWeekNumbers)
735
+ html += '<th class="week">' + this.locale.weekLabel + '</th>';
430
736
 
431
- if (this.singleDatePicker) {
432
- first.show();
433
- second.hide();
737
+ $.each(this.locale.daysOfWeek, function(index, dayOfWeek) {
738
+ html += '<th>' + dayOfWeek + '</th>';
739
+ });
740
+
741
+ html += '</tr>';
742
+ html += '</thead>';
743
+ html += '<tbody>';
744
+
745
+ //adjust maxDate to reflect the dateLimit setting in order to
746
+ //grey out end dates beyond the dateLimit
747
+ if (this.endDate == null && this.dateLimit) {
748
+ var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day');
749
+ if (!maxDate || maxLimit.isBefore(maxDate)) {
750
+ maxDate = maxLimit;
434
751
  }
435
752
  }
436
753
 
437
- if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
438
- this.container.addClass('show-calendar');
754
+ for (var row = 0; row < 6; row++) {
755
+ html += '<tr>';
756
+
757
+ // add week number
758
+ if (this.showWeekNumbers)
759
+ html += '<td class="week">' + calendar[row][0].week() + '</td>';
760
+ else if (this.showISOWeekNumbers)
761
+ html += '<td class="week">' + calendar[row][0].isoWeek() + '</td>';
762
+
763
+ for (var col = 0; col < 7; col++) {
764
+
765
+ var classes = [];
766
+
767
+ //highlight today's date
768
+ if (calendar[row][col].isSame(new Date(), "day"))
769
+ classes.push('today');
770
+
771
+ //highlight weekends
772
+ if (calendar[row][col].isoWeekday() > 5)
773
+ classes.push('weekend');
774
+
775
+ //grey out the dates in other months displayed at beginning and end of this calendar
776
+ if (calendar[row][col].month() != calendar[1][1].month())
777
+ classes.push('off');
778
+
779
+ //don't allow selection of dates before the minimum date
780
+ if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day'))
781
+ classes.push('off', 'disabled');
782
+
783
+ //don't allow selection of dates after the maximum date
784
+ if (maxDate && calendar[row][col].isAfter(maxDate, 'day'))
785
+ classes.push('off', 'disabled');
786
+
787
+ //don't allow selection of date if a custom function decides it's invalid
788
+ if (this.isInvalidDate(calendar[row][col]))
789
+ classes.push('off', 'disabled');
790
+
791
+ //highlight the currently selected start date
792
+ if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD'))
793
+ classes.push('active', 'start-date');
794
+
795
+ //highlight the currently selected end date
796
+ if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD'))
797
+ classes.push('active', 'end-date');
798
+
799
+ //highlight dates in-between the selected dates
800
+ if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate)
801
+ classes.push('in-range');
802
+
803
+ var cname = '', disabled = false;
804
+ for (var i = 0; i < classes.length; i++) {
805
+ cname += classes[i] + ' ';
806
+ if (classes[i] == 'disabled')
807
+ disabled = true;
808
+ }
809
+ if (!disabled)
810
+ cname += 'available';
811
+
812
+ html += '<td class="' + cname.replace(/^\s+|\s+$/g, '') + '" data-title="' + 'r' + row + 'c' + col + '">' + calendar[row][col].date() + '</td>';
813
+
814
+ }
815
+ html += '</tr>';
439
816
  }
440
817
 
441
- this.container.addClass('opens' + this.opens);
818
+ html += '</tbody>';
819
+ html += '</table>';
442
820
 
443
- this.updateView();
444
- this.updateCalendars();
821
+ this.container.find('.calendar.' + side + ' .calendar-table').html(html);
445
822
 
446
823
  },
447
824
 
448
- setStartDate: function(startDate) {
449
- if (typeof startDate === 'string')
450
- this.startDate = moment(startDate, this.format).zone(this.timeZone);
825
+ renderTimePicker: function(side) {
451
826
 
452
- if (typeof startDate === 'object')
453
- this.startDate = moment(startDate);
827
+ var html, selected, minDate, maxDate = this.maxDate;
454
828
 
455
- if (!this.timePicker)
456
- this.startDate = this.startDate.startOf('day');
829
+ if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate)))
830
+ maxDate = this.startDate.clone().add(this.dateLimit);
457
831
 
458
- this.oldStartDate = this.startDate.clone();
832
+ if (side == 'left') {
833
+ selected = this.startDate.clone();
834
+ minDate = this.minDate;
835
+ } else if (side == 'right') {
836
+ selected = this.endDate ? this.endDate.clone() : this.previousRightTime.clone();
837
+ minDate = this.startDate;
459
838
 
460
- this.updateView();
461
- this.updateCalendars();
462
- this.updateInputText();
463
- },
839
+ //Preserve the time already selected
840
+ var timeSelector = this.container.find('.calendar.right .calendar-time div');
841
+ if (timeSelector.html() != '') {
464
842
 
465
- setEndDate: function(endDate) {
466
- if (typeof endDate === 'string')
467
- this.endDate = moment(endDate, this.format).zone(this.timeZone);
843
+ selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour());
844
+ selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute());
845
+ selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second());
468
846
 
469
- if (typeof endDate === 'object')
470
- this.endDate = moment(endDate);
847
+ if (!this.timePicker24Hour) {
848
+ var ampm = timeSelector.find('.ampmselect option:selected').val();
849
+ if (ampm === 'PM' && selected.hour() < 12)
850
+ selected.hour(selected.hour() + 12);
851
+ if (ampm === 'AM' && selected.hour() === 12)
852
+ selected.hour(0);
853
+ }
471
854
 
472
- if (!this.timePicker)
473
- this.endDate = this.endDate.endOf('day');
855
+ if (selected.isBefore(this.startDate))
856
+ selected = this.startDate.clone();
474
857
 
475
- this.oldEndDate = this.endDate.clone();
858
+ if (selected.isAfter(maxDate))
859
+ selected = maxDate.clone();
476
860
 
477
- this.updateView();
478
- this.updateCalendars();
479
- this.updateInputText();
480
- },
861
+ }
862
+ }
481
863
 
482
- updateView: function () {
483
- this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
484
- this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
485
- this.updateFormInputs();
486
- },
864
+ //
865
+ // hours
866
+ //
487
867
 
488
- updateFormInputs: function () {
489
- this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
490
- this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
868
+ html = '<select class="hourselect">';
491
869
 
492
- if (this.startDate.isSame(this.endDate) || this.startDate.isBefore(this.endDate)) {
493
- this.container.find('button.applyBtn').removeAttr('disabled');
494
- } else {
495
- this.container.find('button.applyBtn').attr('disabled', 'disabled');
870
+ var start = this.timePicker24Hour ? 0 : 1;
871
+ var end = this.timePicker24Hour ? 23 : 12;
872
+
873
+ for (var i = start; i <= end; i++) {
874
+ var i_in_24 = i;
875
+ if (!this.timePicker24Hour)
876
+ i_in_24 = selected.hour() >= 12 ? (i == 12 ? 12 : i + 12) : (i == 12 ? 0 : i);
877
+
878
+ var time = selected.clone().hour(i_in_24);
879
+ var disabled = false;
880
+ if (minDate && time.minute(59).isBefore(minDate))
881
+ disabled = true;
882
+ if (maxDate && time.minute(0).isAfter(maxDate))
883
+ disabled = true;
884
+
885
+ if (i_in_24 == selected.hour() && !disabled) {
886
+ html += '<option value="' + i + '" selected="selected">' + i + '</option>';
887
+ } else if (disabled) {
888
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>';
889
+ } else {
890
+ html += '<option value="' + i + '">' + i + '</option>';
891
+ }
496
892
  }
497
- },
498
893
 
499
- updateFromControl: function () {
500
- if (!this.element.is('input')) return;
501
- if (!this.element.val().length) return;
894
+ html += '</select> ';
502
895
 
503
- var dateString = this.element.val().split(this.separator),
504
- start = null,
505
- end = null;
896
+ //
897
+ // minutes
898
+ //
899
+
900
+ html += ': <select class="minuteselect">';
901
+
902
+ for (var i = 0; i < 60; i += this.timePickerIncrement) {
903
+ var padded = i < 10 ? '0' + i : i;
904
+ var time = selected.clone().minute(i);
905
+
906
+ var disabled = false;
907
+ if (minDate && time.second(59).isBefore(minDate))
908
+ disabled = true;
909
+ if (maxDate && time.second(0).isAfter(maxDate))
910
+ disabled = true;
506
911
 
507
- if(dateString.length === 2) {
508
- start = moment(dateString[0], this.format).zone(this.timeZone);
509
- end = moment(dateString[1], this.format).zone(this.timeZone);
912
+ if (selected.minute() == i && !disabled) {
913
+ html += '<option value="' + i + '" selected="selected">' + padded + '</option>';
914
+ } else if (disabled) {
915
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>';
916
+ } else {
917
+ html += '<option value="' + i + '">' + padded + '</option>';
918
+ }
510
919
  }
511
920
 
512
- if (this.singleDatePicker || start === null || end === null) {
513
- start = moment(this.element.val(), this.format).zone(this.timeZone);
514
- end = start;
921
+ html += '</select> ';
922
+
923
+ //
924
+ // seconds
925
+ //
926
+
927
+ if (this.timePickerSeconds) {
928
+ html += ': <select class="secondselect">';
929
+
930
+ for (var i = 0; i < 60; i++) {
931
+ var padded = i < 10 ? '0' + i : i;
932
+ var time = selected.clone().second(i);
933
+
934
+ var disabled = false;
935
+ if (minDate && time.isBefore(minDate))
936
+ disabled = true;
937
+ if (maxDate && time.isAfter(maxDate))
938
+ disabled = true;
939
+
940
+ if (selected.second() == i && !disabled) {
941
+ html += '<option value="' + i + '" selected="selected">' + padded + '</option>';
942
+ } else if (disabled) {
943
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>';
944
+ } else {
945
+ html += '<option value="' + i + '">' + padded + '</option>';
946
+ }
947
+ }
948
+
949
+ html += '</select> ';
515
950
  }
516
951
 
517
- if (end.isBefore(start)) return;
952
+ //
953
+ // AM/PM
954
+ //
518
955
 
519
- this.oldStartDate = this.startDate.clone();
520
- this.oldEndDate = this.endDate.clone();
956
+ if (!this.timePicker24Hour) {
957
+ html += '<select class="ampmselect">';
521
958
 
522
- this.startDate = start;
523
- this.endDate = end;
959
+ var am_html = '';
960
+ var pm_html = '';
524
961
 
525
- if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
526
- this.notify();
962
+ if (minDate && selected.clone().hour(12).minute(0).second(0).isBefore(minDate))
963
+ am_html = ' disabled="disabled" class="disabled"';
964
+
965
+ if (maxDate && selected.clone().hour(0).minute(0).second(0).isAfter(maxDate))
966
+ pm_html = ' disabled="disabled" class="disabled"';
967
+
968
+ if (selected.hour() >= 12) {
969
+ html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>';
970
+ } else {
971
+ html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>';
972
+ }
973
+
974
+ html += '</select>';
975
+ }
976
+
977
+ this.container.find('.calendar.' + side + ' .calendar-time div').html(html);
527
978
 
528
- this.updateCalendars();
529
979
  },
530
980
 
531
- notify: function () {
532
- this.updateView();
533
- this.cb(this.startDate, this.endDate, this.chosenLabel);
981
+ updateFormInputs: function() {
982
+
983
+ //ignore mouse movements while an above-calendar text input has focus
984
+ if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
985
+ return;
986
+
987
+ this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format));
988
+ if (this.endDate)
989
+ this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format));
990
+
991
+ if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {
992
+ this.container.find('button.applyBtn').removeAttr('disabled');
993
+ } else {
994
+ this.container.find('button.applyBtn').attr('disabled', 'disabled');
995
+ }
996
+
534
997
  },
535
998
 
536
- move: function () {
537
- var parentOffset = { top: 0, left: 0 };
999
+ move: function() {
1000
+ var parentOffset = { top: 0, left: 0 },
1001
+ containerTop;
538
1002
  var parentRightEdge = $(window).width();
539
1003
  if (!this.parentEl.is('body')) {
540
1004
  parentOffset = {
@@ -544,9 +1008,15 @@
544
1008
  parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
545
1009
  }
546
1010
 
1011
+ if (this.drops == 'up')
1012
+ containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
1013
+ else
1014
+ containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
1015
+ this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup');
1016
+
547
1017
  if (this.opens == 'left') {
548
1018
  this.container.css({
549
- top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
1019
+ top: containerTop,
550
1020
  right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),
551
1021
  left: 'auto'
552
1022
  });
@@ -558,7 +1028,7 @@
558
1028
  }
559
1029
  } else if (this.opens == 'center') {
560
1030
  this.container.css({
561
- top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
1031
+ top: containerTop,
562
1032
  left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
563
1033
  - this.container.outerWidth() / 2,
564
1034
  right: 'auto'
@@ -571,7 +1041,7 @@
571
1041
  }
572
1042
  } else {
573
1043
  this.container.css({
574
- top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
1044
+ top: containerTop,
575
1045
  left: this.element.offset().left - parentOffset.left,
576
1046
  right: 'auto'
577
1047
  });
@@ -584,23 +1054,12 @@
584
1054
  }
585
1055
  },
586
1056
 
587
- toggle: function (e) {
588
- if (this.element.hasClass('active')) {
589
- this.hide();
590
- } else {
591
- this.show();
592
- }
593
- },
594
-
595
- show: function (e) {
1057
+ show: function(e) {
596
1058
  if (this.isShowing) return;
597
1059
 
598
- this.element.addClass('active');
599
- this.container.show();
600
- this.move();
601
-
602
1060
  // Create a click proxy that is private to this instance of datepicker, for unbinding
603
- this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this);
1061
+ this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this);
1062
+
604
1063
  // Bind global datepicker mousedown for hiding and
605
1064
  $(document)
606
1065
  .on('mousedown.daterangepicker', this._outsideClickProxy)
@@ -611,55 +1070,65 @@
611
1070
  // and also close when focus changes to outside the picker (eg. tabbing between controls)
612
1071
  .on('focusin.daterangepicker', this._outsideClickProxy);
613
1072
 
614
- this.isShowing = true;
615
- this.element.trigger('show.daterangepicker', this);
616
- },
1073
+ // Reposition the picker if the window is resized while it's open
1074
+ $(window).on('resize.daterangepicker', $.proxy(function(e) { this.move(e); }, this));
617
1075
 
618
- outsideClick: function (e) {
619
- var target = $(e.target);
620
- // if the page is clicked anywhere except within the daterangerpicker/button
621
- // itself then call this.hide()
622
- if (
623
- // ie modal dialog fix
624
- e.type == "focusin" ||
625
- target.closest(this.element).length ||
626
- target.closest(this.container).length ||
627
- target.closest('.calendar-date').length
628
- ) return;
629
- this.hide();
1076
+ this.oldStartDate = this.startDate.clone();
1077
+ this.oldEndDate = this.endDate.clone();
1078
+ this.previousRightTime = this.endDate.clone();
1079
+
1080
+ this.updateView();
1081
+ this.container.show();
1082
+ this.move();
1083
+ this.element.trigger('show.daterangepicker', this);
1084
+ this.isShowing = true;
630
1085
  },
631
1086
 
632
- hide: function (e) {
1087
+ hide: function(e) {
633
1088
  if (!this.isShowing) return;
634
1089
 
635
- $(document)
636
- .off('.daterangepicker');
637
-
638
- this.element.removeClass('active');
639
- this.container.hide();
1090
+ //incomplete date selection, revert to last values
1091
+ if (!this.endDate) {
1092
+ this.startDate = this.oldStartDate.clone();
1093
+ this.endDate = this.oldEndDate.clone();
1094
+ }
640
1095
 
1096
+ //if a new date range was selected, invoke the user callback function
641
1097
  if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
642
- this.notify();
1098
+ this.callback(this.startDate, this.endDate, this.chosenLabel);
643
1099
 
644
- this.oldStartDate = this.startDate.clone();
645
- this.oldEndDate = this.endDate.clone();
1100
+ //if picker is attached to a text input, update it
1101
+ this.updateElement();
646
1102
 
647
- this.isShowing = false;
1103
+ $(document).off('.daterangepicker');
1104
+ $(window).off('.daterangepicker');
1105
+ this.container.hide();
648
1106
  this.element.trigger('hide.daterangepicker', this);
1107
+ this.isShowing = false;
649
1108
  },
650
1109
 
651
- enterRange: function (e) {
652
- // mouse pointer has entered a range label
653
- var label = e.target.innerHTML;
654
- if (label == this.locale.customRangeLabel) {
655
- this.updateView();
1110
+ toggle: function(e) {
1111
+ if (this.isShowing) {
1112
+ this.hide();
656
1113
  } else {
657
- var dates = this.ranges[label];
658
- this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.format));
659
- this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.format));
1114
+ this.show();
660
1115
  }
661
1116
  },
662
1117
 
1118
+ outsideClick: function(e) {
1119
+ var target = $(e.target);
1120
+ // if the page is clicked anywhere except within the daterangerpicker/button
1121
+ // itself then call this.hide()
1122
+ if (
1123
+ // ie modal dialog fix
1124
+ e.type == "focusin" ||
1125
+ target.closest(this.element).length ||
1126
+ target.closest(this.container).length ||
1127
+ target.closest('.calendar-table').length
1128
+ ) return;
1129
+ this.hide();
1130
+ },
1131
+
663
1132
  showCalendars: function() {
664
1133
  this.container.addClass('show-calendar');
665
1134
  this.move();
@@ -671,46 +1140,30 @@
671
1140
  this.element.trigger('hideCalendar.daterangepicker', this);
672
1141
  },
673
1142
 
674
- // when a date is typed into the start to end date textboxes
675
- inputsChanged: function (e) {
676
- var el = $(e.target);
677
- var date = moment(el.val(), this.format);
678
- if (!date.isValid()) return;
1143
+ hoverRange: function(e) {
679
1144
 
680
- var startDate, endDate;
681
- if (el.attr('name') === 'daterangepicker_start') {
682
- startDate = date;
683
- endDate = this.endDate;
684
- } else {
685
- startDate = this.startDate;
686
- endDate = date;
687
- }
688
- this.setCustomDates(startDate, endDate);
689
- },
1145
+ //ignore mouse movements while an above-calendar text input has focus
1146
+ if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1147
+ return;
690
1148
 
691
- inputsKeydown: function(e) {
692
- if (e.keyCode === 13) {
693
- this.inputsChanged(e);
694
- this.notify();
1149
+ var label = e.target.innerHTML;
1150
+ if (label == this.locale.customRangeLabel) {
1151
+ this.updateView();
1152
+ } else {
1153
+ var dates = this.ranges[label];
1154
+ this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format));
1155
+ this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format));
695
1156
  }
696
- },
697
1157
 
698
- updateInputText: function() {
699
- if (this.element.is('input') && !this.singleDatePicker) {
700
- this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
701
- } else if (this.element.is('input')) {
702
- this.element.val(this.endDate.format(this.format));
703
- }
704
1158
  },
705
1159
 
706
- clickRange: function (e) {
1160
+ clickRange: function(e) {
707
1161
  var label = e.target.innerHTML;
708
1162
  this.chosenLabel = label;
709
1163
  if (label == this.locale.customRangeLabel) {
710
1164
  this.showCalendars();
711
1165
  } else {
712
1166
  var dates = this.ranges[label];
713
-
714
1167
  this.startDate = dates[0];
715
1168
  this.endDate = dates[1];
716
1169
 
@@ -719,127 +1172,193 @@
719
1172
  this.endDate.endOf('day');
720
1173
  }
721
1174
 
722
- this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
723
- this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
724
- this.updateCalendars();
725
-
726
- this.updateInputText();
727
-
728
- this.hideCalendars();
729
- this.hide();
730
- this.element.trigger('apply.daterangepicker', this);
1175
+ if (!this.alwaysShowCalendars)
1176
+ this.hideCalendars();
1177
+ this.clickApply();
731
1178
  }
732
1179
  },
733
1180
 
734
- clickPrev: function (e) {
1181
+ clickPrev: function(e) {
735
1182
  var cal = $(e.target).parents('.calendar');
736
1183
  if (cal.hasClass('left')) {
737
1184
  this.leftCalendar.month.subtract(1, 'month');
1185
+ if (this.linkedCalendars)
1186
+ this.rightCalendar.month.subtract(1, 'month');
738
1187
  } else {
739
1188
  this.rightCalendar.month.subtract(1, 'month');
740
1189
  }
741
1190
  this.updateCalendars();
742
1191
  },
743
1192
 
744
- clickNext: function (e) {
1193
+ clickNext: function(e) {
745
1194
  var cal = $(e.target).parents('.calendar');
746
1195
  if (cal.hasClass('left')) {
747
1196
  this.leftCalendar.month.add(1, 'month');
748
1197
  } else {
749
1198
  this.rightCalendar.month.add(1, 'month');
1199
+ if (this.linkedCalendars)
1200
+ this.leftCalendar.month.add(1, 'month');
750
1201
  }
751
1202
  this.updateCalendars();
752
1203
  },
753
1204
 
754
- hoverDate: function (e) {
1205
+ hoverDate: function(e) {
1206
+
1207
+ //ignore mouse movements while an above-calendar text input has focus
1208
+ if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1209
+ return;
1210
+
1211
+ //ignore dates that can't be selected
1212
+ if (!$(e.target).hasClass('available')) return;
1213
+
1214
+ //have the text inputs above calendars reflect the date being hovered over
755
1215
  var title = $(e.target).attr('data-title');
756
1216
  var row = title.substr(1, 1);
757
1217
  var col = title.substr(3, 1);
758
1218
  var cal = $(e.target).parents('.calendar');
1219
+ var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
759
1220
 
760
- if (cal.hasClass('left')) {
761
- this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].format(this.format));
1221
+ if (this.endDate) {
1222
+ this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format));
762
1223
  } else {
763
- this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].format(this.format));
1224
+ this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format));
764
1225
  }
765
- },
766
1226
 
767
- setCustomDates: function(startDate, endDate) {
768
- this.chosenLabel = this.locale.customRangeLabel;
769
- if (startDate.isAfter(endDate)) {
770
- var difference = this.endDate.diff(this.startDate);
771
- endDate = moment(startDate).add(difference, 'ms');
1227
+ //highlight the dates between the start date and the date being hovered as a potential end date
1228
+ var leftCalendar = this.leftCalendar;
1229
+ var rightCalendar = this.rightCalendar;
1230
+ var startDate = this.startDate;
1231
+ if (!this.endDate) {
1232
+ this.container.find('.calendar td').each(function(index, el) {
1233
+
1234
+ //skip week numbers, only look at dates
1235
+ if ($(el).hasClass('week')) return;
1236
+
1237
+ var title = $(el).attr('data-title');
1238
+ var row = title.substr(1, 1);
1239
+ var col = title.substr(3, 1);
1240
+ var cal = $(el).parents('.calendar');
1241
+ var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
1242
+
1243
+ if (dt.isAfter(startDate) && dt.isBefore(date)) {
1244
+ $(el).addClass('in-range');
1245
+ } else {
1246
+ $(el).removeClass('in-range');
1247
+ }
1248
+
1249
+ });
772
1250
  }
773
- this.startDate = startDate;
774
- this.endDate = endDate;
775
1251
 
776
- this.updateView();
777
- this.updateCalendars();
778
1252
  },
779
1253
 
780
- clickDate: function (e) {
1254
+ clickDate: function(e) {
1255
+
1256
+ if (!$(e.target).hasClass('available')) return;
1257
+
781
1258
  var title = $(e.target).attr('data-title');
782
1259
  var row = title.substr(1, 1);
783
1260
  var col = title.substr(3, 1);
784
1261
  var cal = $(e.target).parents('.calendar');
1262
+ var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
785
1263
 
786
- var startDate, endDate;
787
- if (cal.hasClass('left')) {
788
- startDate = this.leftCalendar.calendar[row][col];
789
- endDate = this.endDate;
790
- if (typeof this.dateLimit === 'object') {
791
- var maxDate = moment(startDate).add(this.dateLimit).startOf('day');
792
- if (endDate.isAfter(maxDate)) {
793
- endDate = maxDate;
1264
+ //
1265
+ // this function needs to do a few things:
1266
+ // * alternate between selecting a start and end date for the range,
1267
+ // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date
1268
+ // * if autoapply is enabled, and an end date was chosen, apply the selection
1269
+ // * if single date picker mode, and time picker isn't enabled, apply the selection immediately
1270
+ //
1271
+
1272
+ if (this.endDate || date.isBefore(this.startDate, 'day')) {
1273
+ if (this.timePicker) {
1274
+ var hour = parseInt(this.container.find('.left .hourselect').val(), 10);
1275
+ if (!this.timePicker24Hour) {
1276
+ var ampm = this.container.find('.left .ampmselect').val();
1277
+ if (ampm === 'PM' && hour < 12)
1278
+ hour += 12;
1279
+ if (ampm === 'AM' && hour === 12)
1280
+ hour = 0;
794
1281
  }
1282
+ var minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
1283
+ var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
1284
+ date = date.clone().hour(hour).minute(minute).second(second);
795
1285
  }
1286
+ this.endDate = null;
1287
+ this.setStartDate(date.clone());
1288
+ } else if (!this.endDate && date.isBefore(this.startDate)) {
1289
+ //special case: clicking the same date for start/end,
1290
+ //but the time of the end date is before the start date
1291
+ this.setEndDate(this.startDate.clone());
796
1292
  } else {
797
- startDate = this.startDate;
798
- endDate = this.rightCalendar.calendar[row][col];
799
- if (typeof this.dateLimit === 'object') {
800
- var minDate = moment(endDate).subtract(this.dateLimit).startOf('day');
801
- if (startDate.isBefore(minDate)) {
802
- startDate = minDate;
1293
+ if (this.timePicker) {
1294
+ var hour = parseInt(this.container.find('.right .hourselect').val(), 10);
1295
+ if (!this.timePicker24Hour) {
1296
+ var ampm = this.container.find('.right .ampmselect').val();
1297
+ if (ampm === 'PM' && hour < 12)
1298
+ hour += 12;
1299
+ if (ampm === 'AM' && hour === 12)
1300
+ hour = 0;
803
1301
  }
1302
+ var minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
1303
+ var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
1304
+ date = date.clone().hour(hour).minute(minute).second(second);
1305
+ }
1306
+ this.setEndDate(date.clone());
1307
+ if (this.autoApply) {
1308
+ this.calculateChosenLabel();
1309
+ this.clickApply();
804
1310
  }
805
1311
  }
806
1312
 
807
- if (this.singleDatePicker && cal.hasClass('left')) {
808
- endDate = startDate.clone();
809
- } else if (this.singleDatePicker && cal.hasClass('right')) {
810
- startDate = endDate.clone();
1313
+ if (this.singleDatePicker) {
1314
+ this.setEndDate(this.startDate);
1315
+ if (!this.timePicker)
1316
+ this.clickApply();
811
1317
  }
812
1318
 
813
- cal.find('td').removeClass('active');
814
-
815
- $(e.target).addClass('active');
816
-
817
- this.setCustomDates(startDate, endDate);
1319
+ this.updateView();
818
1320
 
819
- if (!this.timePicker)
820
- endDate.endOf('day');
1321
+ },
821
1322
 
822
- if (this.singleDatePicker && !this.timePicker)
823
- this.clickApply();
1323
+ calculateChosenLabel: function() {
1324
+ var customRange = true;
1325
+ var i = 0;
1326
+ for (var range in this.ranges) {
1327
+ if (this.timePicker) {
1328
+ if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
1329
+ customRange = false;
1330
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1331
+ break;
1332
+ }
1333
+ } else {
1334
+ //ignore times when comparing dates if time picker is not enabled
1335
+ if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
1336
+ customRange = false;
1337
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1338
+ break;
1339
+ }
1340
+ }
1341
+ i++;
1342
+ }
1343
+ if (customRange) {
1344
+ this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
1345
+ this.showCalendars();
1346
+ }
824
1347
  },
825
1348
 
826
- clickApply: function (e) {
827
- this.updateInputText();
1349
+ clickApply: function(e) {
828
1350
  this.hide();
829
1351
  this.element.trigger('apply.daterangepicker', this);
830
1352
  },
831
1353
 
832
- clickCancel: function (e) {
1354
+ clickCancel: function(e) {
833
1355
  this.startDate = this.oldStartDate;
834
1356
  this.endDate = this.oldEndDate;
835
- this.chosenLabel = this.oldChosenLabel;
836
- this.updateView();
837
- this.updateCalendars();
838
1357
  this.hide();
839
1358
  this.element.trigger('cancel.daterangepicker', this);
840
1359
  },
841
1360
 
842
- updateMonthYear: function (e) {
1361
+ monthOrYearChanged: function(e) {
843
1362
  var isLeft = $(e.target).closest('.calendar').hasClass('left'),
844
1363
  leftOrRight = isLeft ? 'left' : 'right',
845
1364
  cal = this.container.find('.calendar.'+leftOrRight);
@@ -848,24 +1367,49 @@
848
1367
  var month = parseInt(cal.find('.monthselect').val(), 10);
849
1368
  var year = cal.find('.yearselect').val();
850
1369
 
851
- this[leftOrRight+'Calendar'].month.month(month).year(year);
1370
+ if (!isLeft) {
1371
+ if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {
1372
+ month = this.startDate.month();
1373
+ year = this.startDate.year();
1374
+ }
1375
+ }
1376
+
1377
+ if (this.minDate) {
1378
+ if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {
1379
+ month = this.minDate.month();
1380
+ year = this.minDate.year();
1381
+ }
1382
+ }
1383
+
1384
+ if (this.maxDate) {
1385
+ if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {
1386
+ month = this.maxDate.month();
1387
+ year = this.maxDate.year();
1388
+ }
1389
+ }
1390
+
1391
+ if (isLeft) {
1392
+ this.leftCalendar.month.month(month).year(year);
1393
+ if (this.linkedCalendars)
1394
+ this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month');
1395
+ } else {
1396
+ this.rightCalendar.month.month(month).year(year);
1397
+ if (this.linkedCalendars)
1398
+ this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month');
1399
+ }
852
1400
  this.updateCalendars();
853
1401
  },
854
1402
 
855
- updateTime: function(e) {
1403
+ timeChanged: function(e) {
856
1404
 
857
1405
  var cal = $(e.target).closest('.calendar'),
858
1406
  isLeft = cal.hasClass('left');
859
1407
 
860
1408
  var hour = parseInt(cal.find('.hourselect').val(), 10);
861
1409
  var minute = parseInt(cal.find('.minuteselect').val(), 10);
862
- var second = 0;
863
-
864
- if (this.timePickerSeconds) {
865
- second = parseInt(cal.find('.secondselect').val(), 10);
866
- }
1410
+ var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0;
867
1411
 
868
- if (this.timePicker12Hour) {
1412
+ if (!this.timePicker24Hour) {
869
1413
  var ampm = cal.find('.ampmselect').val();
870
1414
  if (ampm === 'PM' && hour < 12)
871
1415
  hour += 12;
@@ -878,377 +1422,121 @@
878
1422
  start.hour(hour);
879
1423
  start.minute(minute);
880
1424
  start.second(second);
881
- this.startDate = start;
882
- this.leftCalendar.month.hour(hour).minute(minute).second(second);
883
- if (this.singleDatePicker)
884
- this.endDate = start.clone();
885
- } else {
1425
+ this.setStartDate(start);
1426
+ if (this.singleDatePicker) {
1427
+ this.endDate = this.startDate.clone();
1428
+ } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) {
1429
+ this.setEndDate(start.clone());
1430
+ }
1431
+ } else if (this.endDate) {
886
1432
  var end = this.endDate.clone();
887
1433
  end.hour(hour);
888
1434
  end.minute(minute);
889
1435
  end.second(second);
890
- this.endDate = end;
891
- if (this.singleDatePicker)
892
- this.startDate = end.clone();
893
- this.rightCalendar.month.hour(hour).minute(minute).second(second);
1436
+ this.setEndDate(end);
894
1437
  }
895
1438
 
896
- this.updateView();
1439
+ //update the calendars so all clickable dates reflect the new time component
897
1440
  this.updateCalendars();
898
- },
899
-
900
- updateCalendars: function () {
901
- this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.month(), this.leftCalendar.month.year(), this.leftCalendar.month.hour(), this.leftCalendar.month.minute(), this.leftCalendar.month.second(), 'left');
902
- this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.month(), this.rightCalendar.month.year(), this.rightCalendar.month.hour(), this.rightCalendar.month.minute(), this.rightCalendar.month.second(), 'right');
903
- this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate, 'left'));
904
- this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.singleDatePicker ? this.minDate : this.startDate, this.maxDate, 'right'));
905
-
906
- this.container.find('.ranges li').removeClass('active');
907
- var customRange = true;
908
- var i = 0;
909
- for (var range in this.ranges) {
910
- if (this.timePicker) {
911
- if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
912
- customRange = false;
913
- this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
914
- .addClass('active').html();
915
- }
916
- } else {
917
- //ignore times when comparing dates if time picker is not enabled
918
- if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
919
- customRange = false;
920
- this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
921
- .addClass('active').html();
922
- }
923
- }
924
- i++;
925
- }
926
- if (customRange) {
927
- this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
928
- this.showCalendars();
929
- }
930
- },
931
1441
 
932
- buildCalendar: function (month, year, hour, minute, second, side) {
933
- var daysInMonth = moment([year, month]).daysInMonth();
934
- var firstDay = moment([year, month, 1]);
935
- var lastDay = moment([year, month, daysInMonth]);
936
- var lastMonth = moment(firstDay).subtract(1, 'month').month();
937
- var lastYear = moment(firstDay).subtract(1, 'month').year();
938
-
939
- var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
940
-
941
- var dayOfWeek = firstDay.day();
942
-
943
- var i;
944
-
945
- //initialize a 6 rows x 7 columns array for the calendar
946
- var calendar = [];
947
- calendar.firstDay = firstDay;
948
- calendar.lastDay = lastDay;
1442
+ //update the form inputs above the calendars with the new time
1443
+ this.updateFormInputs();
949
1444
 
950
- for (i = 0; i < 6; i++) {
951
- calendar[i] = [];
952
- }
1445
+ //re-render the time pickers because changing one selection can affect what's enabled in another
1446
+ this.renderTimePicker('left');
1447
+ this.renderTimePicker('right');
953
1448
 
954
- //populate the calendar with date objects
955
- var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
956
- if (startDay > daysInLastMonth)
957
- startDay -= 7;
1449
+ },
958
1450
 
959
- if (dayOfWeek == this.locale.firstDay)
960
- startDay = daysInLastMonth - 6;
1451
+ formInputsChanged: function(e) {
1452
+ var isRight = $(e.target).closest('.calendar').hasClass('right');
1453
+ var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format);
1454
+ var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format);
961
1455
 
962
- var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]).zone(this.timeZone);
1456
+ if (start.isValid() && end.isValid()) {
963
1457
 
964
- var col, row;
965
- for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
966
- if (i > 0 && col % 7 === 0) {
967
- col = 0;
968
- row++;
969
- }
970
- calendar[row][col] = curDate.clone().hour(hour);
971
- curDate.hour(12);
1458
+ if (isRight && end.isBefore(start))
1459
+ start = end.clone();
972
1460
 
973
- if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
974
- calendar[row][col] = this.minDate.clone();
975
- }
1461
+ this.setStartDate(start);
1462
+ this.setEndDate(end);
976
1463
 
977
- if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
978
- calendar[row][col] = this.maxDate.clone();
1464
+ if (isRight) {
1465
+ this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format));
1466
+ } else {
1467
+ this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format));
979
1468
  }
980
1469
 
981
1470
  }
982
1471
 
983
- return calendar;
984
- },
985
-
986
- renderDropdowns: function (selected, minDate, maxDate) {
987
- var currentMonth = selected.month();
988
- var currentYear = selected.year();
989
- var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
990
- var minYear = (minDate && minDate.year()) || (currentYear - 50);
991
-
992
- var monthHtml = '<select class="monthselect">';
993
- var inMinYear = currentYear == minYear;
994
- var inMaxYear = currentYear == maxYear;
995
-
996
- for (var m = 0; m < 12; m++) {
997
- if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
998
- monthHtml += "<option value='" + m + "'" +
999
- (m === currentMonth ? " selected='selected'" : "") +
1000
- ">" + this.locale.monthNames[m] + "</option>";
1001
- }
1002
- }
1003
- monthHtml += "</select>";
1004
-
1005
- var yearHtml = '<select class="yearselect">';
1006
-
1007
- for (var y = minYear; y <= maxYear; y++) {
1008
- yearHtml += '<option value="' + y + '"' +
1009
- (y === currentYear ? ' selected="selected"' : '') +
1010
- '>' + y + '</option>';
1472
+ this.updateCalendars();
1473
+ if (this.timePicker) {
1474
+ this.renderTimePicker('left');
1475
+ this.renderTimePicker('right');
1011
1476
  }
1012
-
1013
- yearHtml += '</select>';
1014
-
1015
- return monthHtml + yearHtml;
1016
1477
  },
1017
1478
 
1018
- renderCalendar: function (calendar, selected, minDate, maxDate, side) {
1019
-
1020
- var html = '<div class="calendar-date">';
1021
- html += '<table class="table-condensed">';
1022
- html += '<thead>';
1023
- html += '<tr>';
1024
-
1025
- // add empty cell for week number
1026
- if (this.showWeekNumbers)
1027
- html += '<th></th>';
1028
-
1029
- if (!minDate || minDate.isBefore(calendar.firstDay)) {
1030
- html += '<th class="prev available"><i class="fa fa-arrow-left icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
1031
- } else {
1032
- html += '<th></th>';
1033
- }
1479
+ elementChanged: function() {
1480
+ if (!this.element.is('input')) return;
1481
+ if (!this.element.val().length) return;
1482
+ if (this.element.val().length < this.locale.format.length) return;
1034
1483
 
1035
- var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
1484
+ var dateString = this.element.val().split(this.locale.separator),
1485
+ start = null,
1486
+ end = null;
1036
1487
 
1037
- if (this.showDropdowns) {
1038
- dateHtml = this.renderDropdowns(calendar[1][1], minDate, maxDate);
1488
+ if (dateString.length === 2) {
1489
+ start = moment(dateString[0], this.locale.format);
1490
+ end = moment(dateString[1], this.locale.format);
1039
1491
  }
1040
1492
 
1041
- html += '<th colspan="5" class="month">' + dateHtml + '</th>';
1042
- if (!maxDate || maxDate.isAfter(calendar.lastDay)) {
1043
- html += '<th class="next available"><i class="fa fa-arrow-right icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
1044
- } else {
1045
- html += '<th></th>';
1493
+ if (this.singleDatePicker || start === null || end === null) {
1494
+ start = moment(this.element.val(), this.locale.format);
1495
+ end = start;
1046
1496
  }
1047
1497
 
1048
- html += '</tr>';
1049
- html += '<tr>';
1050
-
1051
- // add week number label
1052
- if (this.showWeekNumbers)
1053
- html += '<th class="week">' + this.locale.weekLabel + '</th>';
1054
-
1055
- $.each(this.locale.daysOfWeek, function (index, dayOfWeek) {
1056
- html += '<th>' + dayOfWeek + '</th>';
1057
- });
1058
-
1059
- html += '</tr>';
1060
- html += '</thead>';
1061
- html += '<tbody>';
1062
-
1063
- for (var row = 0; row < 6; row++) {
1064
- html += '<tr>';
1065
-
1066
- // add week number
1067
- if (this.showWeekNumbers)
1068
- html += '<td class="week">' + calendar[row][0].week() + '</td>';
1498
+ if (!start.isValid() || !end.isValid()) return;
1069
1499
 
1070
- for (var col = 0; col < 7; col++) {
1071
- var cname = 'available ';
1072
- cname += (calendar[row][col].month() == calendar[1][1].month()) ? '' : 'off';
1073
-
1074
- if ((minDate && calendar[row][col].isBefore(minDate, 'day')) || (maxDate && calendar[row][col].isAfter(maxDate, 'day'))) {
1075
- cname = ' off disabled ';
1076
- } else if (calendar[row][col].format('YYYY-MM-DD') == selected.format('YYYY-MM-DD')) {
1077
- cname += ' active ';
1078
- if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) {
1079
- cname += ' start-date ';
1080
- }
1081
- if (calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) {
1082
- cname += ' end-date ';
1083
- }
1084
- } else if (calendar[row][col] >= this.startDate && calendar[row][col] <= this.endDate) {
1085
- cname += ' in-range ';
1086
- if (calendar[row][col].isSame(this.startDate)) { cname += ' start-date '; }
1087
- if (calendar[row][col].isSame(this.endDate)) { cname += ' end-date '; }
1088
- }
1500
+ this.setStartDate(start);
1501
+ this.setEndDate(end);
1502
+ this.updateView();
1503
+ },
1089
1504
 
1090
- var title = 'r' + row + 'c' + col;
1091
- html += '<td class="' + cname.replace(/\s+/g, ' ').replace(/^\s?(.*?)\s?$/, '$1') + '" data-title="' + title + '">' + calendar[row][col].date() + '</td>';
1092
- }
1093
- html += '</tr>';
1505
+ keydown: function(e) {
1506
+ //hide on tab or enter
1507
+ if ((e.keyCode === 9) || (e.keyCode === 13)) {
1508
+ this.hide();
1094
1509
  }
1510
+ },
1095
1511
 
1096
- html += '</tbody>';
1097
- html += '</table>';
1098
- html += '</div>';
1099
-
1100
- var i;
1101
- if (this.timePicker) {
1102
-
1103
- html += '<div class="calendar-time">';
1104
- html += '<select class="hourselect">';
1105
-
1106
- // Disallow selections before the minDate or after the maxDate
1107
- var min_hour = 0;
1108
- var max_hour = 23;
1109
-
1110
- if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == minDate.format('YYYY-MM-DD')) {
1111
- min_hour = minDate.hour();
1112
- if (selected.hour() < min_hour)
1113
- selected.hour(min_hour);
1114
- if (this.timePicker12Hour && min_hour >= 12 && selected.hour() >= 12)
1115
- min_hour -= 12;
1116
- if (this.timePicker12Hour && min_hour == 12)
1117
- min_hour = 1;
1118
- }
1119
-
1120
- if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == maxDate.format('YYYY-MM-DD')) {
1121
- max_hour = maxDate.hour();
1122
- if (selected.hour() > max_hour)
1123
- selected.hour(max_hour);
1124
- if (this.timePicker12Hour && max_hour >= 12 && selected.hour() >= 12)
1125
- max_hour -= 12;
1126
- }
1127
-
1128
- var start = 0;
1129
- var end = 23;
1130
- var selected_hour = selected.hour();
1131
- if (this.timePicker12Hour) {
1132
- start = 1;
1133
- end = 12;
1134
- if (selected_hour >= 12)
1135
- selected_hour -= 12;
1136
- if (selected_hour === 0)
1137
- selected_hour = 12;
1138
- }
1139
-
1140
- for (i = start; i <= end; i++) {
1141
-
1142
- if (i == selected_hour) {
1143
- html += '<option value="' + i + '" selected="selected">' + i + '</option>';
1144
- } else if (i < min_hour || i > max_hour) {
1145
- html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>';
1146
- } else {
1147
- html += '<option value="' + i + '">' + i + '</option>';
1148
- }
1149
- }
1150
-
1151
- html += '</select> : ';
1152
-
1153
- html += '<select class="minuteselect">';
1154
-
1155
- // Disallow selections before the minDate or after the maxDate
1156
- var min_minute = 0;
1157
- var max_minute = 59;
1158
-
1159
- if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD h A') == minDate.format('YYYY-MM-DD h A')) {
1160
- min_minute = minDate.minute();
1161
- if (selected.minute() < min_minute)
1162
- selected.minute(min_minute);
1163
- }
1164
-
1165
- if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD h A') == maxDate.format('YYYY-MM-DD h A')) {
1166
- max_minute = maxDate.minute();
1167
- if (selected.minute() > max_minute)
1168
- selected.minute(max_minute);
1169
- }
1170
-
1171
- for (i = 0; i < 60; i += this.timePickerIncrement) {
1172
- var num = i;
1173
- if (num < 10)
1174
- num = '0' + num;
1175
- if (i == selected.minute()) {
1176
- html += '<option value="' + i + '" selected="selected">' + num + '</option>';
1177
- } else if (i < min_minute || i > max_minute) {
1178
- html += '<option value="' + i + '" disabled="disabled" class="disabled">' + num + '</option>';
1179
- } else {
1180
- html += '<option value="' + i + '">' + num + '</option>';
1181
- }
1182
- }
1183
-
1184
- html += '</select> ';
1185
-
1186
- if (this.timePickerSeconds) {
1187
- html += ': <select class="secondselect">';
1188
-
1189
- for (i = 0; i < 60; i += this.timePickerIncrement) {
1190
- var num = i;
1191
- if (num < 10)
1192
- num = '0' + num;
1193
- if (i == selected.second()) {
1194
- html += '<option value="' + i + '" selected="selected">' + num + '</option>';
1195
- } else {
1196
- html += '<option value="' + i + '">' + num + '</option>';
1197
- }
1198
- }
1199
-
1200
- html += '</select>';
1201
- }
1202
-
1203
- if (this.timePicker12Hour) {
1204
- html += '<select class="ampmselect">';
1205
-
1206
- // Disallow selection before the minDate or after the maxDate
1207
- var am_html = '';
1208
- var pm_html = '';
1209
-
1210
- if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == minDate.format('YYYY-MM-DD') && minDate.hour() >= 12) {
1211
- am_html = ' disabled="disabled" class="disabled"';
1212
- }
1213
-
1214
- if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == maxDate.format('YYYY-MM-DD') && maxDate.hour() < 12) {
1215
- pm_html = ' disabled="disabled" class="disabled"';
1216
- }
1217
-
1218
- if (selected.hour() >= 12) {
1219
- html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>';
1220
- } else {
1221
- html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>';
1222
- }
1223
- html += '</select>';
1224
- }
1225
-
1226
- html += '</div>';
1227
-
1512
+ updateElement: function() {
1513
+ if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
1514
+ this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
1515
+ this.element.trigger('change');
1516
+ } else if (this.element.is('input') && this.autoUpdateInput) {
1517
+ this.element.val(this.startDate.format(this.locale.format));
1518
+ this.element.trigger('change');
1228
1519
  }
1229
-
1230
- return html;
1231
-
1232
1520
  },
1233
1521
 
1234
1522
  remove: function() {
1235
-
1236
1523
  this.container.remove();
1237
1524
  this.element.off('.daterangepicker');
1238
- this.element.removeData('daterangepicker');
1239
-
1525
+ this.element.removeData();
1240
1526
  }
1241
1527
 
1242
1528
  };
1243
1529
 
1244
- $.fn.daterangepicker = function (options, cb) {
1245
- this.each(function () {
1530
+ $.fn.daterangepicker = function(options, callback) {
1531
+ this.each(function() {
1246
1532
  var el = $(this);
1247
1533
  if (el.data('daterangepicker'))
1248
1534
  el.data('daterangepicker').remove();
1249
- el.data('daterangepicker', new DateRangePicker(el, options, cb));
1535
+ el.data('daterangepicker', new DateRangePicker(el, options, callback));
1250
1536
  });
1251
1537
  return this;
1252
1538
  };
1253
1539
 
1540
+ return DateRangePicker;
1541
+
1254
1542
  }));