blazer 2.2.5 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of blazer might be problematic. Click here for more details.

Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +113 -29
  5. data/app/assets/javascripts/blazer/Chart.js +13794 -12099
  6. data/app/assets/javascripts/blazer/Sortable.js +3695 -1526
  7. data/app/assets/javascripts/blazer/chartkick.js +296 -46
  8. data/app/assets/javascripts/blazer/daterangepicker.js +194 -269
  9. data/app/assets/javascripts/blazer/jquery.js +1150 -642
  10. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +621 -287
  11. data/app/assets/javascripts/blazer/moment.js +5085 -2460
  12. data/app/assets/stylesheets/blazer/daterangepicker.css +394 -253
  13. data/app/controllers/blazer/base_controller.rb +4 -4
  14. data/app/controllers/blazer/dashboards_controller.rb +5 -2
  15. data/app/controllers/blazer/queries_controller.rb +11 -2
  16. data/app/controllers/blazer/uploads_controller.rb +147 -0
  17. data/app/mailers/blazer/slack_notifier.rb +1 -1
  18. data/app/models/blazer/query.rb +1 -0
  19. data/app/models/blazer/upload.rb +11 -0
  20. data/app/models/blazer/uploads_connection.rb +7 -0
  21. data/app/views/blazer/_nav.html.erb +3 -0
  22. data/app/views/blazer/_variables.html.erb +3 -1
  23. data/app/views/blazer/checks/_form.html.erb +1 -1
  24. data/app/views/blazer/checks/index.html.erb +3 -0
  25. data/app/views/blazer/dashboards/_form.html.erb +1 -1
  26. data/app/views/blazer/dashboards/show.html.erb +1 -1
  27. data/app/views/blazer/queries/home.html.erb +3 -0
  28. data/app/views/blazer/queries/run.html.erb +5 -5
  29. data/app/views/blazer/queries/show.html.erb +3 -1
  30. data/app/views/blazer/uploads/_form.html.erb +27 -0
  31. data/app/views/blazer/uploads/edit.html.erb +3 -0
  32. data/app/views/blazer/uploads/index.html.erb +55 -0
  33. data/app/views/blazer/uploads/new.html.erb +3 -0
  34. data/config/routes.rb +5 -0
  35. data/lib/blazer.rb +20 -0
  36. data/lib/blazer/adapters/influxdb_adapter.rb +45 -0
  37. data/lib/blazer/adapters/sql_adapter.rb +1 -1
  38. data/lib/blazer/result.rb +2 -21
  39. data/lib/blazer/version.rb +1 -1
  40. data/lib/generators/blazer/templates/config.yml.tt +6 -0
  41. data/lib/generators/blazer/templates/install.rb.tt +4 -3
  42. data/lib/generators/blazer/templates/uploads.rb.tt +10 -0
  43. data/lib/generators/blazer/uploads_generator.rb +18 -0
  44. data/lib/tasks/blazer.rake +9 -0
  45. data/licenses/LICENSE-ace.txt +24 -0
  46. data/licenses/LICENSE-bootstrap.txt +21 -0
  47. data/licenses/LICENSE-chart.js.txt +9 -0
  48. data/licenses/LICENSE-chartkick.js.txt +22 -0
  49. data/licenses/LICENSE-daterangepicker.txt +21 -0
  50. data/licenses/LICENSE-fuzzysearch.txt +20 -0
  51. data/licenses/LICENSE-highlight.js.txt +29 -0
  52. data/licenses/LICENSE-jquery-ujs.txt +20 -0
  53. data/licenses/LICENSE-jquery.txt +20 -0
  54. data/licenses/LICENSE-moment-timezone.txt +20 -0
  55. data/licenses/LICENSE-moment.txt +22 -0
  56. data/licenses/LICENSE-selectize.txt +202 -0
  57. data/licenses/LICENSE-sortable.txt +21 -0
  58. data/licenses/LICENSE-stickytableheaders.txt +20 -0
  59. data/licenses/LICENSE-stupidtable.txt +19 -0
  60. data/licenses/LICENSE-vue.txt +21 -0
  61. metadata +33 -7
@@ -1,16 +1,17 @@
1
1
  /**
2
- * @version: 2.1.27
2
+ * @version: 3.1
3
3
  * @author: Dan Grossman http://www.dangrossman.info/
4
- * @copyright: Copyright (c) 2012-2017 Dan Grossman. All rights reserved.
4
+ * @copyright: Copyright (c) 2012-2019 Dan Grossman. All rights reserved.
5
5
  * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
6
6
  * @website: http://www.daterangepicker.com/
7
7
  */
8
- // Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js
8
+ // Following the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js
9
9
  (function (root, factory) {
10
10
  if (typeof define === 'function' && define.amd) {
11
11
  // AMD. Make globaly available as well
12
12
  define(['moment', 'jquery'], function (moment, jquery) {
13
13
  if (!jquery.fn) jquery.fn = {}; // webpack server rendering
14
+ if (typeof moment !== 'function' && moment.hasOwnProperty('default')) moment = moment['default']
14
15
  return factory(moment, jquery);
15
16
  });
16
17
  } else if (typeof module === 'object' && module.exports) {
@@ -37,10 +38,12 @@
37
38
  this.endDate = moment().endOf('day');
38
39
  this.minDate = false;
39
40
  this.maxDate = false;
40
- this.dateLimit = false;
41
+ this.maxSpan = false;
41
42
  this.autoApply = false;
42
43
  this.singleDatePicker = false;
43
44
  this.showDropdowns = false;
45
+ this.minYear = moment().subtract(100, 'year').format('YYYY');
46
+ this.maxYear = moment().add(100, 'year').format('YYYY');
44
47
  this.showWeekNumbers = false;
45
48
  this.showISOWeekNumbers = false;
46
49
  this.showCustomRangeLabel = true;
@@ -62,8 +65,8 @@
62
65
  this.drops = 'up';
63
66
 
64
67
  this.buttonClasses = 'btn btn-sm';
65
- this.applyClass = 'btn-success';
66
- this.cancelClass = 'btn-default';
68
+ this.applyButtonClasses = 'btn-primary';
69
+ this.cancelButtonClasses = 'btn-default';
67
70
 
68
71
  this.locale = {
69
72
  direction: 'ltr',
@@ -95,34 +98,21 @@
95
98
 
96
99
  //html template for the picker UI
97
100
  if (typeof options.template !== 'string' && !(options.template instanceof $))
98
- options.template = '<div class="daterangepicker dropdown-menu">' +
99
- '<div class="calendar left">' +
100
- '<div class="daterangepicker_input">' +
101
- '<input class="input-mini form-control" type="text" name="daterangepicker_start" value="" />' +
102
- '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
103
- '<div class="calendar-time">' +
104
- '<div></div>' +
105
- '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
106
- '</div>' +
107
- '</div>' +
101
+ options.template =
102
+ '<div class="daterangepicker">' +
103
+ '<div class="ranges"></div>' +
104
+ '<div class="drp-calendar left">' +
108
105
  '<div class="calendar-table"></div>' +
106
+ '<div class="calendar-time"></div>' +
109
107
  '</div>' +
110
- '<div class="calendar right">' +
111
- '<div class="daterangepicker_input">' +
112
- '<input class="input-mini form-control" type="text" name="daterangepicker_end" value="" />' +
113
- '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
114
- '<div class="calendar-time">' +
115
- '<div></div>' +
116
- '<i class="fa fa-clock-o glyphicon glyphicon-time"></i>' +
117
- '</div>' +
118
- '</div>' +
108
+ '<div class="drp-calendar right">' +
119
109
  '<div class="calendar-table"></div>' +
110
+ '<div class="calendar-time"></div>' +
120
111
  '</div>' +
121
- '<div class="ranges">' +
122
- '<div class="range_inputs">' +
123
- '<button class="applyBtn" disabled="disabled" type="button"></button> ' +
124
- '<button class="cancelBtn" type="button"></button>' +
125
- '</div>' +
112
+ '<div class="drp-buttons">' +
113
+ '<span class="drp-selected"></span>' +
114
+ '<button class="cancelBtn" type="button"></button>' +
115
+ '<button class="applyBtn" disabled="disabled" type="button"></button> ' +
126
116
  '</div>' +
127
117
  '</div>';
128
118
 
@@ -204,14 +194,23 @@
204
194
  if (this.maxDate && this.endDate.isAfter(this.maxDate))
205
195
  this.endDate = this.maxDate.clone();
206
196
 
207
- if (typeof options.applyClass === 'string')
208
- this.applyClass = options.applyClass;
197
+ if (typeof options.applyButtonClasses === 'string')
198
+ this.applyButtonClasses = options.applyButtonClasses;
209
199
 
210
- if (typeof options.cancelClass === 'string')
211
- this.cancelClass = options.cancelClass;
200
+ if (typeof options.applyClass === 'string') //backwards compat
201
+ this.applyButtonClasses = options.applyClass;
212
202
 
213
- if (typeof options.dateLimit === 'object')
214
- this.dateLimit = options.dateLimit;
203
+ if (typeof options.cancelButtonClasses === 'string')
204
+ this.cancelButtonClasses = options.cancelButtonClasses;
205
+
206
+ if (typeof options.cancelClass === 'string') //backwards compat
207
+ this.cancelButtonClasses = options.cancelClass;
208
+
209
+ if (typeof options.maxSpan === 'object')
210
+ this.maxSpan = options.maxSpan;
211
+
212
+ if (typeof options.dateLimit === 'object') //backwards compat
213
+ this.maxSpan = options.dateLimit;
215
214
 
216
215
  if (typeof options.opens === 'string')
217
216
  this.opens = options.opens;
@@ -234,6 +233,12 @@
234
233
  if (typeof options.showDropdowns === 'boolean')
235
234
  this.showDropdowns = options.showDropdowns;
236
235
 
236
+ if (typeof options.minYear === 'number')
237
+ this.minYear = options.minYear;
238
+
239
+ if (typeof options.maxYear === 'number')
240
+ this.maxYear = options.maxYear;
241
+
237
242
  if (typeof options.showCustomRangeLabel === 'boolean')
238
243
  this.showCustomRangeLabel = options.showCustomRangeLabel;
239
244
 
@@ -286,7 +291,7 @@
286
291
 
287
292
  //if no start/end dates set, check if an input element contains initial values
288
293
  if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
289
- if ($(this.element).is('input[type=text]')) {
294
+ if ($(this.element).is(':text')) {
290
295
  var val = $(this.element).val(),
291
296
  split = val.split(this.locale.separator);
292
297
 
@@ -319,20 +324,20 @@
319
324
  else
320
325
  end = moment(options.ranges[range][1]);
321
326
 
322
- // If the start or end date exceed those allowed by the minDate or dateLimit
327
+ // If the start or end date exceed those allowed by the minDate or maxSpan
323
328
  // options, shorten the range to the allowable period.
324
329
  if (this.minDate && start.isBefore(this.minDate))
325
330
  start = this.minDate.clone();
326
331
 
327
332
  var maxDate = this.maxDate;
328
- if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate))
329
- maxDate = start.clone().add(this.dateLimit);
333
+ if (this.maxSpan && maxDate && start.clone().add(this.maxSpan).isAfter(maxDate))
334
+ maxDate = start.clone().add(this.maxSpan);
330
335
  if (maxDate && end.isAfter(maxDate))
331
336
  end = maxDate.clone();
332
337
 
333
338
  // If the end of the range is before the minimum or the start of the range is
334
339
  // after the maximum, don't display this range option at all.
335
- if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day'))
340
+ if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day'))
336
341
  || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))
337
342
  continue;
338
343
 
@@ -369,22 +374,20 @@
369
374
  if (this.timePicker && this.autoApply)
370
375
  this.autoApply = false;
371
376
 
372
- if (this.autoApply && typeof options.ranges !== 'object') {
373
- this.container.find('.ranges').hide();
374
- } else if (this.autoApply) {
375
- this.container.find('.applyBtn, .cancelBtn').addClass('hide');
377
+ if (this.autoApply) {
378
+ this.container.addClass('auto-apply');
376
379
  }
377
380
 
381
+ if (typeof options.ranges === 'object')
382
+ this.container.addClass('show-ranges');
383
+
378
384
  if (this.singleDatePicker) {
379
385
  this.container.addClass('single');
380
- this.container.find('.calendar.left').addClass('single');
381
- this.container.find('.calendar.left').show();
382
- this.container.find('.calendar.right').hide();
383
- this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide();
384
- if (this.timePicker) {
385
- this.container.find('.ranges ul').hide();
386
- } else {
387
- this.container.find('.ranges').hide();
386
+ this.container.find('.drp-calendar.left').addClass('single');
387
+ this.container.find('.drp-calendar.left').show();
388
+ this.container.find('.drp-calendar.right').hide();
389
+ if (!this.timePicker && this.autoApply) {
390
+ this.container.addClass('auto-apply');
388
391
  }
389
392
  }
390
393
 
@@ -394,17 +397,12 @@
394
397
 
395
398
  this.container.addClass('opens' + this.opens);
396
399
 
397
- //swap the position of the predefined ranges if opens right
398
- if (typeof options.ranges !== 'undefined' && this.opens == 'right') {
399
- this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() );
400
- }
401
-
402
400
  //apply CSS classes and labels to buttons
403
401
  this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses);
404
- if (this.applyClass.length)
405
- this.container.find('.applyBtn').addClass(this.applyClass);
406
- if (this.cancelClass.length)
407
- this.container.find('.cancelBtn').addClass(this.cancelClass);
402
+ if (this.applyButtonClasses.length)
403
+ this.container.find('.applyBtn').addClass(this.applyButtonClasses);
404
+ if (this.cancelButtonClasses.length)
405
+ this.container.find('.cancelBtn').addClass(this.cancelButtonClasses);
408
406
  this.container.find('.applyBtn').html(this.locale.applyLabel);
409
407
  this.container.find('.cancelBtn').html(this.locale.cancelLabel);
410
408
 
@@ -412,27 +410,21 @@
412
410
  // event listeners
413
411
  //
414
412
 
415
- this.container.find('.calendar')
413
+ this.container.find('.drp-calendar')
416
414
  .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
417
415
  .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
418
416
  .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
419
417
  .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
420
- .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
421
418
  .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))
422
419
  .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
423
- .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))
424
- .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this))
425
- .on('focus.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsFocused, this))
426
- .on('blur.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsBlurred, this))
427
- .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))
428
- .on('keydown.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsKeydown, this));
420
+ .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this));
429
421
 
430
422
  this.container.find('.ranges')
423
+ .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this));
424
+
425
+ this.container.find('.drp-buttons')
431
426
  .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
432
- .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
433
- .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
434
- .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this))
435
- .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
427
+ .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this));
436
428
 
437
429
  if (this.element.is('input') || this.element.is('button')) {
438
430
  this.element.on({
@@ -450,13 +442,7 @@
450
442
  // if attached to a text input, set the initial value
451
443
  //
452
444
 
453
- if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
454
- this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
455
- this.element.trigger('change');
456
- } else if (this.element.is('input') && this.autoUpdateInput) {
457
- this.element.val(this.startDate.format(this.locale.format));
458
- this.element.trigger('change');
459
- }
445
+ this.updateElement();
460
446
 
461
447
  };
462
448
 
@@ -503,7 +489,7 @@
503
489
  this.endDate = moment(endDate);
504
490
 
505
491
  if (!this.timePicker)
506
- this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second');
492
+ this.endDate = this.endDate.endOf('day');
507
493
 
508
494
  if (this.timePicker && this.timePickerIncrement)
509
495
  this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
@@ -514,11 +500,13 @@
514
500
  if (this.maxDate && this.endDate.isAfter(this.maxDate))
515
501
  this.endDate = this.maxDate.clone();
516
502
 
517
- if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate))
518
- this.endDate = this.startDate.clone().add(this.dateLimit);
503
+ if (this.maxSpan && this.startDate.clone().add(this.maxSpan).isBefore(this.endDate))
504
+ this.endDate = this.startDate.clone().add(this.maxSpan);
519
505
 
520
506
  this.previousRightTime = this.endDate.clone();
521
507
 
508
+ this.container.find('.drp-selected').html(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
509
+
522
510
  if (!this.isShowing)
523
511
  this.updateElement();
524
512
 
@@ -538,18 +526,13 @@
538
526
  this.renderTimePicker('left');
539
527
  this.renderTimePicker('right');
540
528
  if (!this.endDate) {
541
- this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled');
529
+ this.container.find('.right .calendar-time select').prop('disabled', true).addClass('disabled');
542
530
  } else {
543
- this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled');
531
+ this.container.find('.right .calendar-time select').prop('disabled', false).removeClass('disabled');
544
532
  }
545
533
  }
546
- if (this.endDate) {
547
- this.container.find('input[name="daterangepicker_end"]').removeClass('active');
548
- this.container.find('input[name="daterangepicker_start"]').addClass('active');
549
- } else {
550
- this.container.find('input[name="daterangepicker_end"]').addClass('active');
551
- this.container.find('input[name="daterangepicker_start"]').removeClass('active');
552
- }
534
+ if (this.endDate)
535
+ this.container.find('.drp-selected').html(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
553
536
  this.updateMonthsInView();
554
537
  this.updateCalendars();
555
538
  this.updateFormInputs();
@@ -593,6 +576,9 @@
593
576
  if (this.endDate) {
594
577
  hour = parseInt(this.container.find('.left .hourselect').val(), 10);
595
578
  minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
579
+ if (isNaN(minute)) {
580
+ minute = parseInt(this.container.find('.left .minuteselect option:last').val(), 10);
581
+ }
596
582
  second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
597
583
  if (!this.timePicker24Hour) {
598
584
  var ampm = this.container.find('.left .ampmselect').val();
@@ -604,6 +590,9 @@
604
590
  } else {
605
591
  hour = parseInt(this.container.find('.right .hourselect').val(), 10);
606
592
  minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
593
+ if (isNaN(minute)) {
594
+ minute = parseInt(this.container.find('.right .minuteselect option:last').val(), 10);
595
+ }
607
596
  second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
608
597
  if (!this.timePicker24Hour) {
609
598
  var ampm = this.container.find('.right .ampmselect').val();
@@ -710,7 +699,7 @@
710
699
  html += '<th></th>';
711
700
 
712
701
  if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {
713
- html += '<th class="prev available"><i class="fa fa-' + arrow.left + ' glyphicon glyphicon-' + arrow.left + '"></i></th>';
702
+ html += '<th class="prev available"><span></span></th>';
714
703
  } else {
715
704
  html += '<th></th>';
716
705
  }
@@ -720,14 +709,14 @@
720
709
  if (this.showDropdowns) {
721
710
  var currentMonth = calendar[1][1].month();
722
711
  var currentYear = calendar[1][1].year();
723
- var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
724
- var minYear = (minDate && minDate.year()) || (currentYear - 50);
712
+ var maxYear = (maxDate && maxDate.year()) || (this.maxYear);
713
+ var minYear = (minDate && minDate.year()) || (this.minYear);
725
714
  var inMinYear = currentYear == minYear;
726
715
  var inMaxYear = currentYear == maxYear;
727
716
 
728
717
  var monthHtml = '<select class="monthselect">';
729
718
  for (var m = 0; m < 12; m++) {
730
- if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
719
+ if ((!inMinYear || (minDate && m >= minDate.month())) && (!inMaxYear || (maxDate && m <= maxDate.month()))) {
731
720
  monthHtml += "<option value='" + m + "'" +
732
721
  (m === currentMonth ? " selected='selected'" : "") +
733
722
  ">" + this.locale.monthNames[m] + "</option>";
@@ -752,7 +741,7 @@
752
741
 
753
742
  html += '<th colspan="5" class="month">' + dateHtml + '</th>';
754
743
  if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {
755
- html += '<th class="next available"><i class="fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '"></i></th>';
744
+ html += '<th class="next available"><span></span></th>';
756
745
  } else {
757
746
  html += '<th></th>';
758
747
  }
@@ -772,10 +761,10 @@
772
761
  html += '</thead>';
773
762
  html += '<tbody>';
774
763
 
775
- //adjust maxDate to reflect the dateLimit setting in order to
776
- //grey out end dates beyond the dateLimit
777
- if (this.endDate == null && this.dateLimit) {
778
- var maxLimit = this.startDate.clone().add(this.dateLimit).endOf('day');
764
+ //adjust maxDate to reflect the maxSpan setting in order to
765
+ //grey out end dates beyond the maxSpan
766
+ if (this.endDate == null && this.maxSpan) {
767
+ var maxLimit = this.startDate.clone().add(this.maxSpan).endOf('day');
779
768
  if (!maxDate || maxLimit.isBefore(maxDate)) {
780
769
  maxDate = maxLimit;
781
770
  }
@@ -804,7 +793,7 @@
804
793
 
805
794
  //grey out the dates in other months displayed at beginning and end of this calendar
806
795
  if (calendar[row][col].month() != calendar[1][1].month())
807
- classes.push('off');
796
+ classes.push('off', 'ends');
808
797
 
809
798
  //don't allow selection of dates before the minimum date
810
799
  if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day'))
@@ -857,7 +846,7 @@
857
846
  html += '</tbody>';
858
847
  html += '</table>';
859
848
 
860
- this.container.find('.calendar.' + side + ' .calendar-table').html(html);
849
+ this.container.find('.drp-calendar.' + side + ' .calendar-table').html(html);
861
850
 
862
851
  },
863
852
 
@@ -869,8 +858,8 @@
869
858
 
870
859
  var html, selected, minDate, maxDate = this.maxDate;
871
860
 
872
- if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate)))
873
- maxDate = this.startDate.clone().add(this.dateLimit);
861
+ if (this.maxSpan && (!this.maxDate || this.startDate.clone().add(this.maxSpan).isBefore(this.maxDate)))
862
+ maxDate = this.startDate.clone().add(this.maxSpan);
874
863
 
875
864
  if (side == 'left') {
876
865
  selected = this.startDate.clone();
@@ -880,12 +869,12 @@
880
869
  minDate = this.startDate;
881
870
 
882
871
  //Preserve the time already selected
883
- var timeSelector = this.container.find('.calendar.right .calendar-time div');
872
+ var timeSelector = this.container.find('.drp-calendar.right .calendar-time');
884
873
  if (timeSelector.html() != '') {
885
874
 
886
- selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour());
887
- selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute());
888
- selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second());
875
+ selected.hour(!isNaN(selected.hour()) ? selected.hour() : timeSelector.find('.hourselect option:selected').val());
876
+ selected.minute(!isNaN(selected.minute()) ? selected.minute() : timeSelector.find('.minuteselect option:selected').val());
877
+ selected.second(!isNaN(selected.second()) ? selected.second() : timeSelector.find('.secondselect option:selected').val());
889
878
 
890
879
  if (!this.timePicker24Hour) {
891
880
  var ampm = timeSelector.find('.ampmselect option:selected').val();
@@ -1018,31 +1007,25 @@
1018
1007
  html += '</select>';
1019
1008
  }
1020
1009
 
1021
- this.container.find('.calendar.' + side + ' .calendar-time div').html(html);
1010
+ this.container.find('.drp-calendar.' + side + ' .calendar-time').html(html);
1022
1011
 
1023
1012
  },
1024
1013
 
1025
1014
  updateFormInputs: function() {
1026
1015
 
1027
- //ignore mouse movements while an above-calendar text input has focus
1028
- if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1029
- return;
1030
-
1031
- this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.locale.format));
1032
- if (this.endDate)
1033
- this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.locale.format));
1034
-
1035
1016
  if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {
1036
- this.container.find('button.applyBtn').removeAttr('disabled');
1017
+ this.container.find('button.applyBtn').prop('disabled', false);
1037
1018
  } else {
1038
- this.container.find('button.applyBtn').attr('disabled', 'disabled');
1019
+ this.container.find('button.applyBtn').prop('disabled', true);
1039
1020
  }
1040
1021
 
1041
1022
  },
1042
1023
 
1043
1024
  move: function() {
1044
1025
  var parentOffset = { top: 0, left: 0 },
1045
- containerTop;
1026
+ containerTop,
1027
+ drops = this.drops;
1028
+
1046
1029
  var parentRightEdge = $(window).width();
1047
1030
  if (!this.parentEl.is('body')) {
1048
1031
  parentOffset = {
@@ -1052,48 +1035,83 @@
1052
1035
  parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
1053
1036
  }
1054
1037
 
1055
- if (this.drops == 'up')
1038
+ switch (drops) {
1039
+ case 'auto':
1040
+ containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
1041
+ if (containerTop + this.container.outerHeight() >= this.parentEl[0].scrollHeight) {
1042
+ containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
1043
+ drops = 'up';
1044
+ }
1045
+ break;
1046
+ case 'up':
1056
1047
  containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
1057
- else
1048
+ break;
1049
+ default:
1058
1050
  containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
1059
- this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('dropup');
1051
+ break;
1052
+ }
1053
+
1054
+ // Force the container to it's actual width
1055
+ this.container.css({
1056
+ top: 0,
1057
+ left: 0,
1058
+ right: 'auto'
1059
+ });
1060
+ var containerWidth = this.container.outerWidth();
1061
+
1062
+ this.container.toggleClass('drop-up', drops == 'up');
1060
1063
 
1061
1064
  if (this.opens == 'left') {
1062
- this.container.css({
1063
- top: containerTop,
1064
- right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),
1065
- left: 'auto'
1066
- });
1067
- if (this.container.offset().left < 0) {
1065
+ var containerRight = parentRightEdge - this.element.offset().left - this.element.outerWidth();
1066
+ if (containerWidth + containerRight > $(window).width()) {
1068
1067
  this.container.css({
1068
+ top: containerTop,
1069
1069
  right: 'auto',
1070
1070
  left: 9
1071
1071
  });
1072
+ } else {
1073
+ this.container.css({
1074
+ top: containerTop,
1075
+ right: containerRight,
1076
+ left: 'auto'
1077
+ });
1072
1078
  }
1073
1079
  } else if (this.opens == 'center') {
1074
- this.container.css({
1075
- top: containerTop,
1076
- left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
1077
- - this.container.outerWidth() / 2,
1078
- right: 'auto'
1079
- });
1080
- if (this.container.offset().left < 0) {
1080
+ var containerLeft = this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
1081
+ - containerWidth / 2;
1082
+ if (containerLeft < 0) {
1081
1083
  this.container.css({
1084
+ top: containerTop,
1082
1085
  right: 'auto',
1083
1086
  left: 9
1084
1087
  });
1088
+ } else if (containerLeft + containerWidth > $(window).width()) {
1089
+ this.container.css({
1090
+ top: containerTop,
1091
+ left: 'auto',
1092
+ right: 0
1093
+ });
1094
+ } else {
1095
+ this.container.css({
1096
+ top: containerTop,
1097
+ left: containerLeft,
1098
+ right: 'auto'
1099
+ });
1085
1100
  }
1086
1101
  } else {
1087
- this.container.css({
1088
- top: containerTop,
1089
- left: this.element.offset().left - parentOffset.left,
1090
- right: 'auto'
1091
- });
1092
- if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
1102
+ var containerLeft = this.element.offset().left - parentOffset.left;
1103
+ if (containerLeft + containerWidth > $(window).width()) {
1093
1104
  this.container.css({
1105
+ top: containerTop,
1094
1106
  left: 'auto',
1095
1107
  right: 0
1096
1108
  });
1109
+ } else {
1110
+ this.container.css({
1111
+ top: containerTop,
1112
+ left: containerLeft,
1113
+ right: 'auto'
1114
+ });
1097
1115
  }
1098
1116
  }
1099
1117
  },
@@ -1139,7 +1157,7 @@
1139
1157
 
1140
1158
  //if a new date range was selected, invoke the user callback function
1141
1159
  if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
1142
- this.callback(this.startDate, this.endDate, this.chosenLabel);
1160
+ this.callback(this.startDate.clone(), this.endDate.clone(), this.chosenLabel);
1143
1161
 
1144
1162
  //if picker is attached to a text input, update it
1145
1163
  this.updateElement();
@@ -1185,24 +1203,6 @@
1185
1203
  this.element.trigger('hideCalendar.daterangepicker', this);
1186
1204
  },
1187
1205
 
1188
- hoverRange: function(e) {
1189
-
1190
- //ignore mouse movements while an above-calendar text input has focus
1191
- if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1192
- return;
1193
-
1194
- var label = e.target.getAttribute('data-range-key');
1195
-
1196
- if (label == this.locale.customRangeLabel) {
1197
- this.updateView();
1198
- } else {
1199
- var dates = this.ranges[label];
1200
- this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.locale.format));
1201
- this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.locale.format));
1202
- }
1203
-
1204
- },
1205
-
1206
1206
  clickRange: function(e) {
1207
1207
  var label = e.target.getAttribute('data-range-key');
1208
1208
  this.chosenLabel = label;
@@ -1225,7 +1225,7 @@
1225
1225
  },
1226
1226
 
1227
1227
  clickPrev: function(e) {
1228
- var cal = $(e.target).parents('.calendar');
1228
+ var cal = $(e.target).parents('.drp-calendar');
1229
1229
  if (cal.hasClass('left')) {
1230
1230
  this.leftCalendar.month.subtract(1, 'month');
1231
1231
  if (this.linkedCalendars)
@@ -1237,7 +1237,7 @@
1237
1237
  },
1238
1238
 
1239
1239
  clickNext: function(e) {
1240
- var cal = $(e.target).parents('.calendar');
1240
+ var cal = $(e.target).parents('.drp-calendar');
1241
1241
  if (cal.hasClass('left')) {
1242
1242
  this.leftCalendar.month.add(1, 'month');
1243
1243
  } else {
@@ -1250,32 +1250,21 @@
1250
1250
 
1251
1251
  hoverDate: function(e) {
1252
1252
 
1253
- //ignore mouse movements while an above-calendar text input has focus
1254
- //if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1255
- // return;
1256
-
1257
1253
  //ignore dates that can't be selected
1258
1254
  if (!$(e.target).hasClass('available')) return;
1259
1255
 
1260
- //have the text inputs above calendars reflect the date being hovered over
1261
1256
  var title = $(e.target).attr('data-title');
1262
1257
  var row = title.substr(1, 1);
1263
1258
  var col = title.substr(3, 1);
1264
- var cal = $(e.target).parents('.calendar');
1259
+ var cal = $(e.target).parents('.drp-calendar');
1265
1260
  var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1266
1261
 
1267
- if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) {
1268
- this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format));
1269
- } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) {
1270
- this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format));
1271
- }
1272
-
1273
1262
  //highlight the dates between the start date and the date being hovered as a potential end date
1274
1263
  var leftCalendar = this.leftCalendar;
1275
1264
  var rightCalendar = this.rightCalendar;
1276
1265
  var startDate = this.startDate;
1277
1266
  if (!this.endDate) {
1278
- this.container.find('.calendar tbody td').each(function(index, el) {
1267
+ this.container.find('.drp-calendar tbody td').each(function(index, el) {
1279
1268
 
1280
1269
  //skip week numbers, only look at dates
1281
1270
  if ($(el).hasClass('week')) return;
@@ -1283,7 +1272,7 @@
1283
1272
  var title = $(el).attr('data-title');
1284
1273
  var row = title.substr(1, 1);
1285
1274
  var col = title.substr(3, 1);
1286
- var cal = $(el).parents('.calendar');
1275
+ var cal = $(el).parents('.drp-calendar');
1287
1276
  var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
1288
1277
 
1289
1278
  if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) {
@@ -1304,7 +1293,7 @@
1304
1293
  var title = $(e.target).attr('data-title');
1305
1294
  var row = title.substr(1, 1);
1306
1295
  var col = title.substr(3, 1);
1307
- var cal = $(e.target).parents('.calendar');
1296
+ var cal = $(e.target).parents('.drp-calendar');
1308
1297
  var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1309
1298
 
1310
1299
  //
@@ -1327,6 +1316,9 @@
1327
1316
  hour = 0;
1328
1317
  }
1329
1318
  var minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
1319
+ if (isNaN(minute)) {
1320
+ minute = parseInt(this.container.find('.left .minuteselect option:last').val(), 10);
1321
+ }
1330
1322
  var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
1331
1323
  date = date.clone().hour(hour).minute(minute).second(second);
1332
1324
  }
@@ -1347,6 +1339,9 @@
1347
1339
  hour = 0;
1348
1340
  }
1349
1341
  var minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
1342
+ if (isNaN(minute)) {
1343
+ minute = parseInt(this.container.find('.right .minuteselect option:last').val(), 10);
1344
+ }
1350
1345
  var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
1351
1346
  date = date.clone().hour(hour).minute(minute).second(second);
1352
1347
  }
@@ -1359,7 +1354,7 @@
1359
1354
 
1360
1355
  if (this.singleDatePicker) {
1361
1356
  this.setEndDate(this.startDate);
1362
- if (!this.timePicker)
1357
+ if (!this.timePicker && this.autoApply)
1363
1358
  this.clickApply();
1364
1359
  }
1365
1360
 
@@ -1375,18 +1370,18 @@
1375
1370
  var i = 0;
1376
1371
  for (var range in this.ranges) {
1377
1372
  if (this.timePicker) {
1378
- var format = this.timePickerSeconds ? "YYYY-MM-DD hh:mm:ss" : "YYYY-MM-DD hh:mm";
1373
+ var format = this.timePickerSeconds ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD HH:mm";
1379
1374
  //ignore times when comparing dates if time picker seconds is not enabled
1380
1375
  if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) {
1381
1376
  customRange = false;
1382
- this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1377
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').attr('data-range-key');
1383
1378
  break;
1384
1379
  }
1385
1380
  } else {
1386
1381
  //ignore times when comparing dates if time picker is not enabled
1387
1382
  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')) {
1388
1383
  customRange = false;
1389
- this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1384
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').attr('data-range-key');
1390
1385
  break;
1391
1386
  }
1392
1387
  }
@@ -1394,7 +1389,7 @@
1394
1389
  }
1395
1390
  if (customRange) {
1396
1391
  if (this.showCustomRangeLabel) {
1397
- this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
1392
+ this.chosenLabel = this.container.find('.ranges li:last').addClass('active').attr('data-range-key');
1398
1393
  } else {
1399
1394
  this.chosenLabel = null;
1400
1395
  }
@@ -1415,9 +1410,9 @@
1415
1410
  },
1416
1411
 
1417
1412
  monthOrYearChanged: function(e) {
1418
- var isLeft = $(e.target).closest('.calendar').hasClass('left'),
1413
+ var isLeft = $(e.target).closest('.drp-calendar').hasClass('left'),
1419
1414
  leftOrRight = isLeft ? 'left' : 'right',
1420
- cal = this.container.find('.calendar.'+leftOrRight);
1415
+ cal = this.container.find('.drp-calendar.'+leftOrRight);
1421
1416
 
1422
1417
  // Month must be Number for new moment versions
1423
1418
  var month = parseInt(cal.find('.monthselect').val(), 10);
@@ -1458,11 +1453,14 @@
1458
1453
 
1459
1454
  timeChanged: function(e) {
1460
1455
 
1461
- var cal = $(e.target).closest('.calendar'),
1456
+ var cal = $(e.target).closest('.drp-calendar'),
1462
1457
  isLeft = cal.hasClass('left');
1463
1458
 
1464
1459
  var hour = parseInt(cal.find('.hourselect').val(), 10);
1465
1460
  var minute = parseInt(cal.find('.minuteselect').val(), 10);
1461
+ if (isNaN(minute)) {
1462
+ minute = parseInt(cal.find('.minuteselect option:last').val(), 10);
1463
+ }
1466
1464
  var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0;
1467
1465
 
1468
1466
  if (!this.timePicker24Hour) {
@@ -1504,81 +1502,6 @@
1504
1502
 
1505
1503
  },
1506
1504
 
1507
- formInputsChanged: function(e) {
1508
- var isRight = $(e.target).closest('.calendar').hasClass('right');
1509
- var start = moment(this.container.find('input[name="daterangepicker_start"]').val(), this.locale.format);
1510
- var end = moment(this.container.find('input[name="daterangepicker_end"]').val(), this.locale.format);
1511
-
1512
- if (start.isValid() && end.isValid()) {
1513
-
1514
- if (isRight && end.isBefore(start))
1515
- start = end.clone();
1516
-
1517
- this.setStartDate(start);
1518
- this.setEndDate(end);
1519
-
1520
- if (isRight) {
1521
- this.container.find('input[name="daterangepicker_start"]').val(this.startDate.format(this.locale.format));
1522
- } else {
1523
- this.container.find('input[name="daterangepicker_end"]').val(this.endDate.format(this.locale.format));
1524
- }
1525
-
1526
- }
1527
-
1528
- this.updateView();
1529
- },
1530
-
1531
- formInputsFocused: function(e) {
1532
-
1533
- // Highlight the focused input
1534
- this.container.find('input[name="daterangepicker_start"], input[name="daterangepicker_end"]').removeClass('active');
1535
- $(e.target).addClass('active');
1536
-
1537
- // Set the state such that if the user goes back to using a mouse,
1538
- // the calendars are aware we're selecting the end of the range, not
1539
- // the start. This allows someone to edit the end of a date range without
1540
- // re-selecting the beginning, by clicking on the end date input then
1541
- // using the calendar.
1542
- var isRight = $(e.target).closest('.calendar').hasClass('right');
1543
- if (isRight) {
1544
- this.endDate = null;
1545
- this.setStartDate(this.startDate.clone());
1546
- this.updateView();
1547
- }
1548
-
1549
- },
1550
-
1551
- formInputsBlurred: function(e) {
1552
-
1553
- // this function has one purpose right now: if you tab from the first
1554
- // text input to the second in the UI, the endDate is nulled so that
1555
- // you can click another, but if you tab out without clicking anything
1556
- // or changing the input value, the old endDate should be retained
1557
-
1558
- if (!this.endDate) {
1559
- var val = this.container.find('input[name="daterangepicker_end"]').val();
1560
- var end = moment(val, this.locale.format);
1561
- if (end.isValid()) {
1562
- this.setEndDate(end);
1563
- this.updateView();
1564
- }
1565
- }
1566
-
1567
- },
1568
-
1569
- formInputsKeydown: function(e) {
1570
- // This function ensures that if the 'enter' key was pressed in the input, then the calendars
1571
- // are updated with the startDate and endDate.
1572
- // This behaviour is automatic in Chrome/Firefox/Edge but not in IE 11 hence why this exists.
1573
- // Other browsers and versions of IE are untested and the behaviour is unknown.
1574
- if (e.keyCode === 13) {
1575
- // Prevent the calendar from being updated twice on Chrome/Firefox/Edge
1576
- e.preventDefault();
1577
- this.formInputsChanged(e);
1578
- }
1579
- },
1580
-
1581
-
1582
1505
  elementChanged: function() {
1583
1506
  if (!this.element.is('input')) return;
1584
1507
  if (!this.element.val().length) return;
@@ -1620,12 +1543,14 @@
1620
1543
  },
1621
1544
 
1622
1545
  updateElement: function() {
1623
- if (this.element.is('input') && !this.singleDatePicker && this.autoUpdateInput) {
1624
- this.element.val(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
1625
- this.element.trigger('change');
1626
- } else if (this.element.is('input') && this.autoUpdateInput) {
1627
- this.element.val(this.startDate.format(this.locale.format));
1628
- this.element.trigger('change');
1546
+ if (this.element.is('input') && this.autoUpdateInput) {
1547
+ var newValue = this.startDate.format(this.locale.format);
1548
+ if (!this.singleDatePicker) {
1549
+ newValue += this.locale.separator + this.endDate.format(this.locale.format);
1550
+ }
1551
+ if (newValue !== this.element.val()) {
1552
+ this.element.val(newValue).trigger('change');
1553
+ }
1629
1554
  }
1630
1555
  },
1631
1556