sql-jarvis 2.0.1 → 2.0.2

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +153 -52
  5. data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +0 -0
  7. data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/images/blazer/favicon.png +0 -0
  11. data/app/assets/javascripts/blazer/Chart.js +4195 -3884
  12. data/app/assets/javascripts/blazer/Sortable.js +1493 -1097
  13. data/app/assets/javascripts/blazer/ace/ace.js +21294 -4
  14. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1991 -3
  15. data/app/assets/javascripts/blazer/ace/mode-sql.js +110 -1
  16. data/app/assets/javascripts/blazer/ace/snippets/sql.js +40 -1
  17. data/app/assets/javascripts/blazer/ace/snippets/text.js +14 -1
  18. data/app/assets/javascripts/blazer/ace/theme-twilight.js +116 -1
  19. data/app/assets/javascripts/blazer/application.js +4 -3
  20. data/app/assets/javascripts/blazer/bootstrap.js +623 -612
  21. data/app/assets/javascripts/blazer/chartkick.js +1769 -1248
  22. data/app/assets/javascripts/blazer/daterangepicker.js +263 -115
  23. data/app/assets/javascripts/blazer/highlight.min.js +3 -0
  24. data/app/assets/javascripts/blazer/{jquery_ujs.js → jquery-ujs.js} +161 -75
  25. data/app/assets/javascripts/blazer/jquery.js +9506 -9450
  26. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +321 -259
  27. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1212 -0
  28. data/app/assets/javascripts/blazer/queries.js +1 -1
  29. data/app/assets/javascripts/blazer/routes.js +3 -0
  30. data/app/assets/javascripts/blazer/selectize.js +3828 -3604
  31. data/app/assets/javascripts/blazer/stupidtable.js +255 -88
  32. data/app/assets/javascripts/blazer/vue.js +8015 -4583
  33. data/app/assets/stylesheets/blazer/application.css +41 -5
  34. data/app/assets/stylesheets/blazer/bootstrap.css.erb +879 -325
  35. data/app/assets/stylesheets/blazer/daterangepicker.css +269 -0
  36. data/app/assets/stylesheets/blazer/selectize.default.css +26 -10
  37. data/app/controllers/blazer/base_controller.rb +7 -0
  38. data/app/controllers/blazer/checks_controller.rb +1 -1
  39. data/app/controllers/blazer/dashboards_controller.rb +0 -4
  40. data/app/controllers/blazer/queries_controller.rb +20 -12
  41. data/app/helpers/blazer/base_helper.rb +1 -1
  42. data/app/mailers/blazer/slack_notifier.rb +76 -0
  43. data/app/models/blazer/check.rb +9 -0
  44. data/app/views/blazer/_nav.html.erb +0 -1
  45. data/app/views/blazer/_variables.html.erb +41 -19
  46. data/app/views/blazer/checks/_form.html.erb +16 -8
  47. data/app/views/blazer/checks/edit.html.erb +2 -0
  48. data/app/views/blazer/checks/index.html.erb +33 -4
  49. data/app/views/blazer/checks/new.html.erb +2 -0
  50. data/app/views/blazer/dashboards/_form.html.erb +4 -4
  51. data/app/views/blazer/dashboards/edit.html.erb +2 -0
  52. data/app/views/blazer/dashboards/new.html.erb +2 -0
  53. data/app/views/blazer/dashboards/show.html.erb +7 -3
  54. data/app/views/blazer/queries/_form.html.erb +11 -6
  55. data/app/views/blazer/queries/docs.html.erb +131 -0
  56. data/app/views/blazer/queries/home.html.erb +12 -3
  57. data/app/views/blazer/queries/run.html.erb +36 -6
  58. data/app/views/blazer/queries/schema.html.erb +46 -8
  59. data/app/views/blazer/queries/show.html.erb +4 -4
  60. data/app/views/layouts/blazer/application.html.erb +3 -3
  61. data/config/routes.rb +5 -1
  62. data/lib/blazer.rb +32 -13
  63. data/lib/blazer/adapters/athena_adapter.rb +1 -1
  64. data/lib/blazer/adapters/elasticsearch_adapter.rb +14 -17
  65. data/lib/blazer/adapters/mongodb_adapter.rb +1 -1
  66. data/lib/blazer/adapters/sql_adapter.rb +7 -1
  67. data/lib/blazer/engine.rb +4 -0
  68. data/lib/blazer/result.rb +62 -29
  69. data/lib/blazer/run_statement_job.rb +6 -9
  70. data/lib/blazer/version.rb +1 -1
  71. data/lib/generators/blazer/templates/config.yml.tt +13 -2
  72. data/lib/generators/blazer/templates/install.rb.tt +1 -0
  73. data/lib/tasks/blazer.rake +1 -0
  74. metadata +33 -37
  75. data/.gitattributes +0 -1
  76. data/.github/ISSUE_TEMPLATE.md +0 -7
  77. data/.gitignore +0 -14
  78. data/Gemfile +0 -7
  79. data/Rakefile +0 -1
  80. data/app/assets/javascripts/blazer/highlight.pack.js +0 -1
  81. data/app/assets/javascripts/blazer/moment-timezone.js +0 -1007
  82. data/app/assets/stylesheets/blazer/daterangepicker-bs3.css +0 -375
  83. data/blazer.gemspec +0 -30
@@ -1,39 +1,33 @@
1
1
  /**
2
- * @version: 2.1.14
2
+ * @version: 2.1.27
3
3
  * @author: Dan Grossman http://www.dangrossman.info/
4
- * @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved.
4
+ * @copyright: Copyright (c) 2012-2017 Dan Grossman. All rights reserved.
5
5
  * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
6
- * @website: https://www.improvely.com/
6
+ * @website: http://www.daterangepicker.com/
7
7
  */
8
-
9
- (function(root, factory) {
10
-
11
- if (typeof define === 'function' && define.amd) {
12
- define(['moment', 'jquery', 'exports'], function(momentjs, $, exports) {
13
- root.daterangepicker = factory(root, exports, momentjs, $);
14
- });
15
-
16
- } else if (typeof exports !== 'undefined') {
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
- }
27
-
28
- factory(root, exports, momentjs, jQuery);
29
-
30
- // Finally, as a browser global.
31
- } else {
32
- root.daterangepicker = factory(root, {}, root.moment || moment, (root.jQuery || root.Zepto || root.ender || root.$));
33
- }
34
-
35
- }(this || {}, function(root, daterangepicker, moment, $) { // 'this' doesn't exist on a server
36
-
8
+ // Follow the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js
9
+ (function (root, factory) {
10
+ if (typeof define === 'function' && define.amd) {
11
+ // AMD. Make globaly available as well
12
+ define(['moment', 'jquery'], function (moment, jquery) {
13
+ if (!jquery.fn) jquery.fn = {}; // webpack server rendering
14
+ return factory(moment, jquery);
15
+ });
16
+ } else if (typeof module === 'object' && module.exports) {
17
+ // Node / Browserify
18
+ //isomorphic issue
19
+ var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined;
20
+ if (!jQuery) {
21
+ jQuery = require('jquery');
22
+ if (!jQuery.fn) jQuery.fn = {};
23
+ }
24
+ var moment = (typeof window != 'undefined' && typeof window.moment != 'undefined') ? window.moment : require('moment');
25
+ module.exports = factory(moment, jQuery);
26
+ } else {
27
+ // Browser globals
28
+ root.daterangepicker = factory(root.moment, root.jQuery);
29
+ }
30
+ }(this, function(moment, $) {
37
31
  var DateRangePicker = function(element, options, cb) {
38
32
 
39
33
  //default settings for options
@@ -48,12 +42,15 @@
48
42
  this.singleDatePicker = false;
49
43
  this.showDropdowns = false;
50
44
  this.showWeekNumbers = false;
45
+ this.showISOWeekNumbers = false;
46
+ this.showCustomRangeLabel = true;
51
47
  this.timePicker = false;
52
48
  this.timePicker24Hour = false;
53
49
  this.timePickerIncrement = 1;
54
50
  this.timePickerSeconds = false;
55
51
  this.linkedCalendars = true;
56
52
  this.autoUpdateInput = true;
53
+ this.alwaysShowCalendars = false;
57
54
  this.ranges = {};
58
55
 
59
56
  this.opens = 'right';
@@ -69,7 +66,8 @@
69
66
  this.cancelClass = 'btn-default';
70
67
 
71
68
  this.locale = {
72
- format: 'MM/DD/YYYY',
69
+ direction: 'ltr',
70
+ format: moment.localeData().longDateFormat('L'),
73
71
  separator: ' - ',
74
72
  applyLabel: 'Apply',
75
73
  cancelLabel: 'Cancel',
@@ -96,11 +94,11 @@
96
94
  options = $.extend(this.element.data(), options);
97
95
 
98
96
  //html template for the picker UI
99
- if (typeof options.template !== 'string')
97
+ if (typeof options.template !== 'string' && !(options.template instanceof $))
100
98
  options.template = '<div class="daterangepicker dropdown-menu">' +
101
99
  '<div class="calendar left">' +
102
100
  '<div class="daterangepicker_input">' +
103
- '<input class="input-mini" type="text" name="daterangepicker_start" value="" />' +
101
+ '<input class="input-mini form-control" type="text" name="daterangepicker_start" value="" />' +
104
102
  '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
105
103
  '<div class="calendar-time">' +
106
104
  '<div></div>' +
@@ -111,7 +109,7 @@
111
109
  '</div>' +
112
110
  '<div class="calendar right">' +
113
111
  '<div class="daterangepicker_input">' +
114
- '<input class="input-mini" type="text" name="daterangepicker_end" value="" />' +
112
+ '<input class="input-mini form-control" type="text" name="daterangepicker_end" value="" />' +
115
113
  '<i class="fa fa-calendar glyphicon glyphicon-calendar"></i>' +
116
114
  '<div class="calendar-time">' +
117
115
  '<div></div>' +
@@ -137,6 +135,9 @@
137
135
 
138
136
  if (typeof options.locale === 'object') {
139
137
 
138
+ if (typeof options.locale.direction === 'string')
139
+ this.locale.direction = options.locale.direction;
140
+
140
141
  if (typeof options.locale.format === 'string')
141
142
  this.locale.format = options.locale.format;
142
143
 
@@ -161,10 +162,15 @@
161
162
  if (typeof options.locale.weekLabel === 'string')
162
163
  this.locale.weekLabel = options.locale.weekLabel;
163
164
 
164
- if (typeof options.locale.customRangeLabel === 'string')
165
- this.locale.customRangeLabel = options.locale.customRangeLabel;
166
-
165
+ if (typeof options.locale.customRangeLabel === 'string'){
166
+ //Support unicode chars in the custom range name.
167
+ var elem = document.createElement('textarea');
168
+ elem.innerHTML = options.locale.customRangeLabel;
169
+ var rangeHtml = elem.value;
170
+ this.locale.customRangeLabel = rangeHtml;
171
+ }
167
172
  }
173
+ this.container.addClass(this.locale.direction);
168
174
 
169
175
  if (typeof options.startDate === 'string')
170
176
  this.startDate = moment(options.startDate, this.locale.format);
@@ -216,6 +222,9 @@
216
222
  if (typeof options.showWeekNumbers === 'boolean')
217
223
  this.showWeekNumbers = options.showWeekNumbers;
218
224
 
225
+ if (typeof options.showISOWeekNumbers === 'boolean')
226
+ this.showISOWeekNumbers = options.showISOWeekNumbers;
227
+
219
228
  if (typeof options.buttonClasses === 'string')
220
229
  this.buttonClasses = options.buttonClasses;
221
230
 
@@ -225,6 +234,9 @@
225
234
  if (typeof options.showDropdowns === 'boolean')
226
235
  this.showDropdowns = options.showDropdowns;
227
236
 
237
+ if (typeof options.showCustomRangeLabel === 'boolean')
238
+ this.showCustomRangeLabel = options.showCustomRangeLabel;
239
+
228
240
  if (typeof options.singleDatePicker === 'boolean') {
229
241
  this.singleDatePicker = options.singleDatePicker;
230
242
  if (this.singleDatePicker)
@@ -255,6 +267,12 @@
255
267
  if (typeof options.isInvalidDate === 'function')
256
268
  this.isInvalidDate = options.isInvalidDate;
257
269
 
270
+ if (typeof options.isCustomDate === 'function')
271
+ this.isCustomDate = options.isCustomDate;
272
+
273
+ if (typeof options.alwaysShowCalendars === 'boolean')
274
+ this.alwaysShowCalendars = options.alwaysShowCalendars;
275
+
258
276
  // update day names order to firstDay
259
277
  if (this.locale.firstDay != 0) {
260
278
  var iterator = this.locale.firstDay;
@@ -307,14 +325,15 @@
307
325
  start = this.minDate.clone();
308
326
 
309
327
  var maxDate = this.maxDate;
310
- if (this.dateLimit && start.clone().add(this.dateLimit).isAfter(maxDate))
328
+ if (this.dateLimit && maxDate && start.clone().add(this.dateLimit).isAfter(maxDate))
311
329
  maxDate = start.clone().add(this.dateLimit);
312
330
  if (maxDate && end.isAfter(maxDate))
313
331
  end = maxDate.clone();
314
332
 
315
333
  // If the end of the range is before the minimum or the start of the range is
316
334
  // after the maximum, don't display this range option at all.
317
- if ((this.minDate && end.isBefore(this.minDate)) || (maxDate && start.isAfter(maxDate)))
335
+ if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day'))
336
+ || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))
318
337
  continue;
319
338
 
320
339
  //Support unicode chars in the range names.
@@ -327,9 +346,11 @@
327
346
 
328
347
  var list = '<ul>';
329
348
  for (range in this.ranges) {
330
- list += '<li>' + range + '</li>';
349
+ list += '<li data-range-key="' + range + '">' + range + '</li>';
350
+ }
351
+ if (this.showCustomRangeLabel) {
352
+ list += '<li data-range-key="' + this.locale.customRangeLabel + '">' + this.locale.customRangeLabel + '</li>';
331
353
  }
332
- list += '<li>' + this.locale.customRangeLabel + '</li>';
333
354
  list += '</ul>';
334
355
  this.container.find('.ranges').prepend(list);
335
356
  }
@@ -359,13 +380,15 @@
359
380
  this.container.find('.calendar.left').addClass('single');
360
381
  this.container.find('.calendar.left').show();
361
382
  this.container.find('.calendar.right').hide();
362
- this.container.find('.daterangepicker_input input, .daterangepicker_input i').hide();
363
- if (!this.timePicker) {
383
+ this.container.find('.daterangepicker_input input, .daterangepicker_input > i').hide();
384
+ if (this.timePicker) {
385
+ this.container.find('.ranges ul').hide();
386
+ } else {
364
387
  this.container.find('.ranges').hide();
365
388
  }
366
389
  }
367
390
 
368
- if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
391
+ if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) {
369
392
  this.container.addClass('show-calendar');
370
393
  }
371
394
 
@@ -373,10 +396,7 @@
373
396
 
374
397
  //swap the position of the predefined ranges if opens right
375
398
  if (typeof options.ranges !== 'undefined' && this.opens == 'right') {
376
- var ranges = this.container.find('.ranges');
377
- var html = ranges.clone();
378
- ranges.remove();
379
- this.container.find('.calendar.left').parent().prepend(html);
399
+ this.container.find('.ranges').prependTo( this.container.find('.calendar.left').parent() );
380
400
  }
381
401
 
382
402
  //apply CSS classes and labels to buttons
@@ -395,15 +415,17 @@
395
415
  this.container.find('.calendar')
396
416
  .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
397
417
  .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
398
- .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
418
+ .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
399
419
  .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
400
420
  .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
401
421
  .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))
402
422
  .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
403
423
  .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))
404
424
  .on('click.daterangepicker', '.daterangepicker_input input', $.proxy(this.showCalendars, this))
405
- //.on('keyup.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, this))
406
- .on('change.daterangepicker', '.daterangepicker_input input', $.proxy(this.formInputsChanged, 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));
407
429
 
408
430
  this.container.find('.ranges')
409
431
  .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
@@ -412,15 +434,16 @@
412
434
  .on('mouseenter.daterangepicker', 'li', $.proxy(this.hoverRange, this))
413
435
  .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
414
436
 
415
- if (this.element.is('input')) {
437
+ if (this.element.is('input') || this.element.is('button')) {
416
438
  this.element.on({
417
439
  'click.daterangepicker': $.proxy(this.show, this),
418
440
  'focus.daterangepicker': $.proxy(this.show, this),
419
441
  'keyup.daterangepicker': $.proxy(this.elementChanged, this),
420
- 'keydown.daterangepicker': $.proxy(this.keydown, this)
442
+ 'keydown.daterangepicker': $.proxy(this.keydown, this) //IE 11 compatibility
421
443
  });
422
444
  } else {
423
445
  this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
446
+ this.element.on('keydown.daterangepicker', $.proxy(this.toggle, this));
424
447
  }
425
448
 
426
449
  //
@@ -454,11 +477,17 @@
454
477
  if (this.timePicker && this.timePickerIncrement)
455
478
  this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
456
479
 
457
- if (this.minDate && this.startDate.isBefore(this.minDate))
458
- this.startDate = this.minDate;
480
+ if (this.minDate && this.startDate.isBefore(this.minDate)) {
481
+ this.startDate = this.minDate.clone();
482
+ if (this.timePicker && this.timePickerIncrement)
483
+ this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
484
+ }
459
485
 
460
- if (this.maxDate && this.startDate.isAfter(this.maxDate))
461
- this.startDate = this.maxDate;
486
+ if (this.maxDate && this.startDate.isAfter(this.maxDate)) {
487
+ this.startDate = this.maxDate.clone();
488
+ if (this.timePicker && this.timePickerIncrement)
489
+ this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
490
+ }
462
491
 
463
492
  if (!this.isShowing)
464
493
  this.updateElement();
@@ -474,7 +503,7 @@
474
503
  this.endDate = moment(endDate);
475
504
 
476
505
  if (!this.timePicker)
477
- this.endDate = this.endDate.endOf('day');
506
+ this.endDate = this.endDate.add(1,'d').startOf('day').subtract(1,'second');
478
507
 
479
508
  if (this.timePicker && this.timePickerIncrement)
480
509
  this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
@@ -483,11 +512,13 @@
483
512
  this.endDate = this.startDate.clone();
484
513
 
485
514
  if (this.maxDate && this.endDate.isAfter(this.maxDate))
486
- this.endDate = this.maxDate;
515
+ this.endDate = this.maxDate.clone();
487
516
 
488
517
  if (this.dateLimit && this.startDate.clone().add(this.dateLimit).isBefore(this.endDate))
489
518
  this.endDate = this.startDate.clone().add(this.dateLimit);
490
519
 
520
+ this.previousRightTime = this.endDate.clone();
521
+
491
522
  if (!this.isShowing)
492
523
  this.updateElement();
493
524
 
@@ -498,6 +529,10 @@
498
529
  return false;
499
530
  },
500
531
 
532
+ isCustomDate: function() {
533
+ return false;
534
+ },
535
+
501
536
  updateView: function() {
502
537
  if (this.timePicker) {
503
538
  this.renderTimePicker('left');
@@ -545,6 +580,10 @@
545
580
  this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
546
581
  }
547
582
  }
583
+ if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) {
584
+ this.rightCalendar.month = this.maxDate.clone().date(2);
585
+ this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month');
586
+ }
548
587
  },
549
588
 
550
589
  updateCalendars: function() {
@@ -585,30 +624,7 @@
585
624
  this.container.find('.ranges li').removeClass('active');
586
625
  if (this.endDate == null) return;
587
626
 
588
- var customRange = true;
589
- var i = 0;
590
- for (var range in this.ranges) {
591
- if (this.timePicker) {
592
- if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
593
- customRange = false;
594
- this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
595
- break;
596
- }
597
- } else {
598
- //ignore times when comparing dates if time picker is not enabled
599
- 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')) {
600
- customRange = false;
601
- this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
602
- break;
603
- }
604
- }
605
- i++;
606
- }
607
- if (customRange) {
608
- this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
609
- this.showCalendars();
610
- }
611
-
627
+ this.calculateChosenLabel();
612
628
  },
613
629
 
614
630
  renderCalendar: function(side) {
@@ -683,17 +699,18 @@
683
699
  var minDate = side == 'left' ? this.minDate : this.startDate;
684
700
  var maxDate = this.maxDate;
685
701
  var selected = side == 'left' ? this.startDate : this.endDate;
702
+ var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'};
686
703
 
687
704
  var html = '<table class="table-condensed">';
688
705
  html += '<thead>';
689
706
  html += '<tr>';
690
707
 
691
708
  // add empty cell for week number
692
- if (this.showWeekNumbers)
709
+ if (this.showWeekNumbers || this.showISOWeekNumbers)
693
710
  html += '<th></th>';
694
711
 
695
712
  if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {
696
- html += '<th class="prev available"><i class="fa fa-chevron-left glyphicon glyphicon-chevron-left"></i></th>';
713
+ html += '<th class="prev available"><i class="fa fa-' + arrow.left + ' glyphicon glyphicon-' + arrow.left + '"></i></th>';
697
714
  } else {
698
715
  html += '<th></th>';
699
716
  }
@@ -735,7 +752,7 @@
735
752
 
736
753
  html += '<th colspan="5" class="month">' + dateHtml + '</th>';
737
754
  if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {
738
- html += '<th class="next available"><i class="fa fa-chevron-right glyphicon glyphicon-chevron-right"></i></th>';
755
+ html += '<th class="next available"><i class="fa fa-' + arrow.right + ' glyphicon glyphicon-' + arrow.right + '"></i></th>';
739
756
  } else {
740
757
  html += '<th></th>';
741
758
  }
@@ -744,7 +761,7 @@
744
761
  html += '<tr>';
745
762
 
746
763
  // add week number label
747
- if (this.showWeekNumbers)
764
+ if (this.showWeekNumbers || this.showISOWeekNumbers)
748
765
  html += '<th class="week">' + this.locale.weekLabel + '</th>';
749
766
 
750
767
  $.each(this.locale.daysOfWeek, function(index, dayOfWeek) {
@@ -770,6 +787,8 @@
770
787
  // add week number
771
788
  if (this.showWeekNumbers)
772
789
  html += '<td class="week">' + calendar[row][0].week() + '</td>';
790
+ else if (this.showISOWeekNumbers)
791
+ html += '<td class="week">' + calendar[row][0].isoWeek() + '</td>';
773
792
 
774
793
  for (var col = 0; col < 7; col++) {
775
794
 
@@ -811,6 +830,15 @@
811
830
  if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate)
812
831
  classes.push('in-range');
813
832
 
833
+ //apply custom classes for this date
834
+ var isCustom = this.isCustomDate(calendar[row][col]);
835
+ if (isCustom !== false) {
836
+ if (typeof isCustom === 'string')
837
+ classes.push(isCustom);
838
+ else
839
+ Array.prototype.push.apply(classes, isCustom);
840
+ }
841
+
814
842
  var cname = '', disabled = false;
815
843
  for (var i = 0; i < classes.length; i++) {
816
844
  cname += classes[i] + ' ';
@@ -835,6 +863,10 @@
835
863
 
836
864
  renderTimePicker: function(side) {
837
865
 
866
+ // Don't bother updating the time picker if it's currently disabled
867
+ // because an end date hasn't been clicked yet
868
+ if (side == 'right' && !this.endDate) return;
869
+
838
870
  var html, selected, minDate, maxDate = this.maxDate;
839
871
 
840
872
  if (this.dateLimit && (!this.maxDate || this.startDate.clone().add(this.dateLimit).isAfter(this.maxDate)))
@@ -844,18 +876,33 @@
844
876
  selected = this.startDate.clone();
845
877
  minDate = this.minDate;
846
878
  } else if (side == 'right') {
847
- selected = this.endDate ? this.endDate.clone() : this.startDate.clone();
879
+ selected = this.endDate.clone();
848
880
  minDate = this.startDate;
849
881
 
850
882
  //Preserve the time already selected
851
883
  var timeSelector = this.container.find('.calendar.right .calendar-time div');
852
884
  if (timeSelector.html() != '') {
853
- selected.hour(timeSelector.find('.hourselect option:selected').val() || selected.hour());
854
- selected.minute(timeSelector.find('.minuteselect option:selected').val() || selected.minute());
855
- selected.second(timeSelector.find('.secondselect option:selected').val() || selected.second());
856
- if (selected.isAfter(maxDate))
857
- selected = maxDate.clone();
885
+
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());
889
+
890
+ if (!this.timePicker24Hour) {
891
+ var ampm = timeSelector.find('.ampmselect option:selected').val();
892
+ if (ampm === 'PM' && selected.hour() < 12)
893
+ selected.hour(selected.hour() + 12);
894
+ if (ampm === 'AM' && selected.hour() === 12)
895
+ selected.hour(0);
896
+ }
897
+
858
898
  }
899
+
900
+ if (selected.isBefore(this.startDate))
901
+ selected = this.startDate.clone();
902
+
903
+ if (maxDate && selected.isAfter(maxDate))
904
+ selected = maxDate.clone();
905
+
859
906
  }
860
907
 
861
908
  //
@@ -1072,6 +1119,7 @@
1072
1119
 
1073
1120
  this.oldStartDate = this.startDate.clone();
1074
1121
  this.oldEndDate = this.endDate.clone();
1122
+ this.previousRightTime = this.endDate.clone();
1075
1123
 
1076
1124
  this.updateView();
1077
1125
  this.container.show();
@@ -1123,6 +1171,7 @@
1123
1171
  target.closest('.calendar-table').length
1124
1172
  ) return;
1125
1173
  this.hide();
1174
+ this.element.trigger('outsideClick.daterangepicker', this);
1126
1175
  },
1127
1176
 
1128
1177
  showCalendars: function() {
@@ -1142,7 +1191,8 @@
1142
1191
  if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1143
1192
  return;
1144
1193
 
1145
- var label = e.target.innerHTML;
1194
+ var label = e.target.getAttribute('data-range-key');
1195
+
1146
1196
  if (label == this.locale.customRangeLabel) {
1147
1197
  this.updateView();
1148
1198
  } else {
@@ -1154,7 +1204,7 @@
1154
1204
  },
1155
1205
 
1156
1206
  clickRange: function(e) {
1157
- var label = e.target.innerHTML;
1207
+ var label = e.target.getAttribute('data-range-key');
1158
1208
  this.chosenLabel = label;
1159
1209
  if (label == this.locale.customRangeLabel) {
1160
1210
  this.showCalendars();
@@ -1168,7 +1218,8 @@
1168
1218
  this.endDate.endOf('day');
1169
1219
  }
1170
1220
 
1171
- this.hideCalendars();
1221
+ if (!this.alwaysShowCalendars)
1222
+ this.hideCalendars();
1172
1223
  this.clickApply();
1173
1224
  }
1174
1225
  },
@@ -1200,8 +1251,8 @@
1200
1251
  hoverDate: function(e) {
1201
1252
 
1202
1253
  //ignore mouse movements while an above-calendar text input has focus
1203
- if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1204
- return;
1254
+ //if (this.container.find('input[name=daterangepicker_start]').is(":focus") || this.container.find('input[name=daterangepicker_end]').is(":focus"))
1255
+ // return;
1205
1256
 
1206
1257
  //ignore dates that can't be selected
1207
1258
  if (!$(e.target).hasClass('available')) return;
@@ -1213,9 +1264,9 @@
1213
1264
  var cal = $(e.target).parents('.calendar');
1214
1265
  var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1215
1266
 
1216
- if (this.endDate) {
1267
+ if (this.endDate && !this.container.find('input[name=daterangepicker_start]').is(":focus")) {
1217
1268
  this.container.find('input[name=daterangepicker_start]').val(date.format(this.locale.format));
1218
- } else {
1269
+ } else if (!this.endDate && !this.container.find('input[name=daterangepicker_end]').is(":focus")) {
1219
1270
  this.container.find('input[name=daterangepicker_end]').val(date.format(this.locale.format));
1220
1271
  }
1221
1272
 
@@ -1224,7 +1275,7 @@
1224
1275
  var rightCalendar = this.rightCalendar;
1225
1276
  var startDate = this.startDate;
1226
1277
  if (!this.endDate) {
1227
- this.container.find('.calendar td').each(function(index, el) {
1278
+ this.container.find('.calendar tbody td').each(function(index, el) {
1228
1279
 
1229
1280
  //skip week numbers, only look at dates
1230
1281
  if ($(el).hasClass('week')) return;
@@ -1235,7 +1286,7 @@
1235
1286
  var cal = $(el).parents('.calendar');
1236
1287
  var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
1237
1288
 
1238
- if (dt.isAfter(startDate) && dt.isBefore(date)) {
1289
+ if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) {
1239
1290
  $(el).addClass('in-range');
1240
1291
  } else {
1241
1292
  $(el).removeClass('in-range');
@@ -1262,13 +1313,14 @@
1262
1313
  // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date
1263
1314
  // * if autoapply is enabled, and an end date was chosen, apply the selection
1264
1315
  // * if single date picker mode, and time picker isn't enabled, apply the selection immediately
1316
+ // * if one of the inputs above the calendars was focused, cancel that manual input
1265
1317
  //
1266
1318
 
1267
- if (this.endDate || date.isBefore(this.startDate)) {
1319
+ if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start
1268
1320
  if (this.timePicker) {
1269
1321
  var hour = parseInt(this.container.find('.left .hourselect').val(), 10);
1270
1322
  if (!this.timePicker24Hour) {
1271
- var ampm = cal.find('.ampmselect').val();
1323
+ var ampm = this.container.find('.left .ampmselect').val();
1272
1324
  if (ampm === 'PM' && hour < 12)
1273
1325
  hour += 12;
1274
1326
  if (ampm === 'AM' && hour === 12)
@@ -1280,7 +1332,11 @@
1280
1332
  }
1281
1333
  this.endDate = null;
1282
1334
  this.setStartDate(date.clone());
1283
- } else {
1335
+ } else if (!this.endDate && date.isBefore(this.startDate)) {
1336
+ //special case: clicking the same date for start/end,
1337
+ //but the time of the end date is before the start date
1338
+ this.setEndDate(this.startDate.clone());
1339
+ } else { // picking end
1284
1340
  if (this.timePicker) {
1285
1341
  var hour = parseInt(this.container.find('.right .hourselect').val(), 10);
1286
1342
  if (!this.timePicker24Hour) {
@@ -1295,8 +1351,10 @@
1295
1351
  date = date.clone().hour(hour).minute(minute).second(second);
1296
1352
  }
1297
1353
  this.setEndDate(date.clone());
1298
- if (this.autoApply)
1299
- this.clickApply();
1354
+ if (this.autoApply) {
1355
+ this.calculateChosenLabel();
1356
+ this.clickApply();
1357
+ }
1300
1358
  }
1301
1359
 
1302
1360
  if (this.singleDatePicker) {
@@ -1307,6 +1365,41 @@
1307
1365
 
1308
1366
  this.updateView();
1309
1367
 
1368
+ //This is to cancel the blur event handler if the mouse was in one of the inputs
1369
+ e.stopPropagation();
1370
+
1371
+ },
1372
+
1373
+ calculateChosenLabel: function () {
1374
+ var customRange = true;
1375
+ var i = 0;
1376
+ for (var range in this.ranges) {
1377
+ if (this.timePicker) {
1378
+ var format = this.timePickerSeconds ? "YYYY-MM-DD hh:mm:ss" : "YYYY-MM-DD hh:mm";
1379
+ //ignore times when comparing dates if time picker seconds is not enabled
1380
+ if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) {
1381
+ customRange = false;
1382
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1383
+ break;
1384
+ }
1385
+ } else {
1386
+ //ignore times when comparing dates if time picker is not enabled
1387
+ 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
+ customRange = false;
1389
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').html();
1390
+ break;
1391
+ }
1392
+ }
1393
+ i++;
1394
+ }
1395
+ if (customRange) {
1396
+ if (this.showCustomRangeLabel) {
1397
+ this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
1398
+ } else {
1399
+ this.chosenLabel = null;
1400
+ }
1401
+ this.showCalendars();
1402
+ }
1310
1403
  },
1311
1404
 
1312
1405
  clickApply: function(e) {
@@ -1432,17 +1525,63 @@
1432
1525
 
1433
1526
  }
1434
1527
 
1435
- this.updateCalendars();
1436
- if (this.timePicker) {
1437
- this.renderTimePicker('left');
1438
- this.renderTimePicker('right');
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();
1439
1547
  }
1548
+
1440
1549
  },
1441
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
+
1442
1582
  elementChanged: function() {
1443
1583
  if (!this.element.is('input')) return;
1444
1584
  if (!this.element.val().length) return;
1445
- if (this.element.val().length < this.locale.format.length) return;
1446
1585
 
1447
1586
  var dateString = this.element.val().split(this.locale.separator),
1448
1587
  start = null,
@@ -1470,6 +1609,14 @@
1470
1609
  if ((e.keyCode === 9) || (e.keyCode === 13)) {
1471
1610
  this.hide();
1472
1611
  }
1612
+
1613
+ //hide on esc and prevent propagation
1614
+ if (e.keyCode === 27) {
1615
+ e.preventDefault();
1616
+ e.stopPropagation();
1617
+
1618
+ this.hide();
1619
+ }
1473
1620
  },
1474
1621
 
1475
1622
  updateElement: function() {
@@ -1491,11 +1638,12 @@
1491
1638
  };
1492
1639
 
1493
1640
  $.fn.daterangepicker = function(options, callback) {
1641
+ var implementOptions = $.extend(true, {}, $.fn.daterangepicker.defaultOptions, options);
1494
1642
  this.each(function() {
1495
1643
  var el = $(this);
1496
1644
  if (el.data('daterangepicker'))
1497
1645
  el.data('daterangepicker').remove();
1498
- el.data('daterangepicker', new DateRangePicker(el, options, callback));
1646
+ el.data('daterangepicker', new DateRangePicker(el, implementOptions, callback));
1499
1647
  });
1500
1648
  return this;
1501
1649
  };