blazer 0.0.5 → 0.0.6

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab48088bf2a8e80e0c931e91203b6e85d07aedf1
4
- data.tar.gz: b75b3e35207e0d5c5840c56c2ec9c8fa37c6c312
3
+ metadata.gz: 9be524f1594e55ef7af3dd5205b614f6f7d3d1cf
4
+ data.tar.gz: d3b1dd938c4087a1148f7788a984677085653e5e
5
5
  SHA512:
6
- metadata.gz: ea4ae2ecbf726aae13fa10cf8a1eac471e2eba3c5fa2402388a4c73285068bb81190f5d765345407f3470576b50fe47086decc02ee41e134306f69be6b1d38a1
7
- data.tar.gz: ee0abc959f7978ed51c0602ef3e6fde127fbd1791c84ed99369fd65c96d858c86a8d7fdffabfed28ad0a11ae84ee83119d206c6f7149b55074ba159aee1c3825
6
+ metadata.gz: 540b05d40528718574d620ccc116738741e219dbf83ea48b0294cf33bcf81541998b45404d93ce607cadabf6cf5f67f11261634e60744c38778f1e43da97f2e3
7
+ data.tar.gz: cfd2266e647e7b8ae7dbd7577bfb3cf417b0b12aa1d456ee906352d9841e1ee275df0e418957ee43b8dab29ad33f0fcbb2666de22d002be24177f82b886234d0
@@ -1,3 +1,9 @@
1
+ ## 0.0.6
2
+
3
+ - Added fork button
4
+ - Fixed trending
5
+ - Fixed time zones for date select
6
+
1
7
  ## 0.0.5
2
8
 
3
9
  - Added support for Rails 4.2
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in blazer.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -10,7 +10,7 @@ Blazer eliminates the need for many admin pages
10
10
 
11
11
  Works with PostgreSQL and MySQL
12
12
 
13
- :tangerine: Battle-tested at [Instacart](https://www.instacart.com)
13
+ :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
14
14
 
15
15
  ## Features
16
16
 
@@ -109,7 +109,7 @@ Change time zone
109
109
  Blazer.time_zone = "Pacific Time (US & Canada)"
110
110
  ```
111
111
 
112
- Change timeout *PostgreSQL only* [master]
112
+ Change timeout *PostgreSQL only*
113
113
 
114
114
  ```ruby
115
115
  Blazer.timeout = 10 # defaults to 15
@@ -133,6 +133,18 @@ Customize user name
133
133
  Blazer.user_name = :first_name
134
134
  ```
135
135
 
136
+ ## Charts
137
+
138
+ Blazer will automatically generate charts based on the types of the columns returned in your query
139
+
140
+ ### Line Chart
141
+
142
+ If there are at least 2 columns and the first is a timestamp and all other columns are numeric, a line chart will be generated
143
+
144
+ ### Pie Chart
145
+
146
+ If there are 2 columns and the first column is a string and the second column is a numeric, a pie chart will be generated
147
+
136
148
  ## TODO
137
149
 
138
150
  - better readme
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
1
  require "bundler/gem_tasks"
2
-
@@ -6,6 +6,7 @@
6
6
  //= require ./selectize
7
7
  //= require ./highlight.pack
8
8
  //= require ./moment
9
+ //= require ./moment-timezone
9
10
  //= require ./daterangepicker
10
11
  //= require chartkick
11
12
  //= require ./ace/ace
@@ -1,12 +1,36 @@
1
1
  /**
2
- * @version: 1.3.7
2
+ * @version: 1.3.19
3
3
  * @author: Dan Grossman http://www.dangrossman.info/
4
- * @date: 2014-04-29
5
- * @copyright: Copyright (c) 2012-2014 Dan Grossman. All rights reserved.
6
- * @license: Licensed under Apache License v2.0. See http://www.apache.org/licenses/LICENSE-2.0
7
- * @website: http://www.improvely.com/
4
+ * @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved.
5
+ * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
6
+ * @website: https://www.improvely.com/
8
7
  */
9
- !function ($, moment) {
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;
19
+ try {
20
+ jQuery = require('jquery');
21
+ } catch (err) {
22
+ jQuery = window.jQuery;
23
+ if (!jQuery) throw new Error('jQuery dependency not found');
24
+ }
25
+
26
+ factory(root, exports, momentjs, jQuery);
27
+
28
+ // Finally, as a browser global.
29
+ } else {
30
+ root.daterangepicker = factory(root, {}, root.moment, (root.jQuery || root.Zepto || root.ender || root.$));
31
+ }
32
+
33
+ }(this, function(root, daterangepicker, moment, $) {
10
34
 
11
35
  var DateRangePicker = function (element, options, cb) {
12
36
 
@@ -16,19 +40,22 @@
16
40
  //element that triggered the date range picker
17
41
  this.element = $(element);
18
42
 
43
+ //tracks visible state
44
+ this.isShowing = false;
45
+
19
46
  //create the picker HTML object
20
47
  var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
21
- '<div class="calendar left"></div>' +
22
- '<div class="calendar right"></div>' +
48
+ '<div class="calendar first left"></div>' +
49
+ '<div class="calendar second right"></div>' +
23
50
  '<div class="ranges">' +
24
51
  '<div class="range_inputs">' +
25
52
  '<div class="daterangepicker_start_input">' +
26
53
  '<label for="daterangepicker_start"></label>' +
27
- '<input class="input-mini" type="text" name="daterangepicker_start" value="" disabled="disabled" />' +
54
+ '<input class="input-mini" type="text" name="daterangepicker_start" value="" />' +
28
55
  '</div>' +
29
56
  '<div class="daterangepicker_end_input">' +
30
57
  '<label for="daterangepicker_end"></label>' +
31
- '<input class="input-mini" type="text" name="daterangepicker_end" value="" disabled="disabled" />' +
58
+ '<input class="input-mini" type="text" name="daterangepicker_end" value="" />' +
32
59
  '</div>' +
33
60
  '<button class="applyBtn" disabled="disabled"></button>&nbsp;' +
34
61
  '<button class="cancelBtn"></button>' +
@@ -65,16 +92,18 @@
65
92
  .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
66
93
  .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
67
94
  .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
68
- .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.enterDate, this))
95
+ .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
69
96
  .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
70
97
  .on('change.daterangepicker', 'select.yearselect', $.proxy(this.updateMonthYear, this))
71
98
  .on('change.daterangepicker', 'select.monthselect', $.proxy(this.updateMonthYear, this))
72
- .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.ampmselect', $.proxy(this.updateTime, this));
99
+ .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.updateTime, this));
73
100
 
74
101
  this.container.find('.ranges')
75
102
  .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
76
103
  .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
77
104
  .on('click.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
105
+ .on('change.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsChanged, this))
106
+ .on('keydown.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsKeydown, this))
78
107
  .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
79
108
  .on('mouseenter.daterangepicker', 'li', $.proxy(this.enterRange, this))
80
109
  .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
@@ -99,6 +128,7 @@
99
128
 
100
129
  this.startDate = moment().startOf('day');
101
130
  this.endDate = moment().endOf('day');
131
+ this.timeZone = moment().utcOffset();
102
132
  this.minDate = false;
103
133
  this.maxDate = false;
104
134
  this.dateLimit = false;
@@ -106,6 +136,7 @@
106
136
  this.showDropdowns = false;
107
137
  this.showWeekNumbers = false;
108
138
  this.timePicker = false;
139
+ this.timePickerSeconds = false;
109
140
  this.timePickerIncrement = 30;
110
141
  this.timePicker12Hour = true;
111
142
  this.singleDatePicker = false;
@@ -115,7 +146,7 @@
115
146
  if (this.element.hasClass('pull-right'))
116
147
  this.opens = 'left';
117
148
 
118
- this.buttonClasses = ['btn', 'btn-small'];
149
+ this.buttonClasses = ['btn', 'btn-small btn-sm'];
119
150
  this.applyClass = 'btn-success';
120
151
  this.cancelClass = 'btn-default';
121
152
 
@@ -129,9 +160,9 @@
129
160
  toLabel: 'To',
130
161
  weekLabel: 'W',
131
162
  customRangeLabel: 'Custom Range',
132
- daysOfWeek: moment()._lang._weekdaysMin.slice(),
133
- monthNames: moment()._lang._monthsShort.slice(),
134
- firstDay: 0
163
+ daysOfWeek: moment.weekdaysMin(),
164
+ monthNames: moment.monthsShort(),
165
+ firstDay: moment.localeData()._week.dow
135
166
  };
136
167
 
137
168
  this.cb = function () { };
@@ -175,7 +206,6 @@
175
206
  if (typeof options.dateLimit === 'object')
176
207
  this.dateLimit = options.dateLimit;
177
208
 
178
- // update day names order to firstDay
179
209
  if (typeof options.locale === 'object') {
180
210
 
181
211
  if (typeof options.locale.daysOfWeek === 'object') {
@@ -189,12 +219,7 @@
189
219
  }
190
220
 
191
221
  if (typeof options.locale.firstDay === 'number') {
192
- this.locale.firstDay = options.locale.firstDay;
193
- var iterator = options.locale.firstDay;
194
- while (iterator > 0) {
195
- this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
196
- iterator--;
197
- }
222
+ this.locale.firstDay = options.locale.firstDay;
198
223
  }
199
224
 
200
225
  if (typeof options.locale.applyLabel === 'string') {
@@ -243,12 +268,19 @@
243
268
 
244
269
  if (typeof options.singleDatePicker === 'boolean') {
245
270
  this.singleDatePicker = options.singleDatePicker;
271
+ if (this.singleDatePicker) {
272
+ this.endDate = this.startDate.clone();
273
+ }
246
274
  }
247
275
 
248
276
  if (typeof options.timePicker === 'boolean') {
249
277
  this.timePicker = options.timePicker;
250
278
  }
251
279
 
280
+ if (typeof options.timePickerSeconds === 'boolean') {
281
+ this.timePickerSeconds = options.timePickerSeconds;
282
+ }
283
+
252
284
  if (typeof options.timePickerIncrement === 'number') {
253
285
  this.timePickerIncrement = options.timePickerIncrement;
254
286
  }
@@ -257,18 +289,29 @@
257
289
  this.timePicker12Hour = options.timePicker12Hour;
258
290
  }
259
291
 
292
+ // update day names order to firstDay
293
+ if (this.locale.firstDay != 0) {
294
+ var iterator = this.locale.firstDay;
295
+ while (iterator > 0) {
296
+ this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
297
+ iterator--;
298
+ }
299
+ }
300
+
260
301
  var start, end, range;
261
302
 
262
303
  //if no start/end dates set, check if an input element contains initial values
263
304
  if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
264
305
  if ($(this.element).is('input[type=text]')) {
265
- var val = $(this.element).val();
266
- var split = val.split(this.separator);
306
+ var val = $(this.element).val(),
307
+ split = val.split(this.separator);
308
+
267
309
  start = end = null;
310
+
268
311
  if (split.length == 2) {
269
312
  start = moment(split[0], this.format);
270
313
  end = moment(split[1], this.format);
271
- } else if (this.singleDatePicker) {
314
+ } else if (this.singleDatePicker && val !== "") {
272
315
  start = moment(val, this.format);
273
316
  end = moment(val, this.format);
274
317
  }
@@ -279,11 +322,27 @@
279
322
  }
280
323
  }
281
324
 
325
+ // 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)
326
+ if (typeof options.timeZone === 'string' || typeof options.timeZone === 'number') {
327
+ this.timeZone = options.timeZone;
328
+ this.startDate.utcOffset(this.timeZone);
329
+ this.endDate.utcOffset(this.timeZone);
330
+ } else {
331
+ this.timeZone = moment(this.startDate).utcOffset();
332
+ }
333
+
282
334
  if (typeof options.ranges === 'object') {
283
335
  for (range in options.ranges) {
284
336
 
285
- start = moment(options.ranges[range][0]);
286
- end = moment(options.ranges[range][1]);
337
+ if (typeof options.ranges[range][0] === 'string')
338
+ start = moment(options.ranges[range][0], this.format);
339
+ else
340
+ start = moment(options.ranges[range][0]);
341
+
342
+ if (typeof options.ranges[range][1] === 'string')
343
+ end = moment(options.ranges[range][1], this.format);
344
+ else
345
+ end = moment(options.ranges[range][1]);
287
346
 
288
347
  // If we have a min/max date set, bound this range
289
348
  // to it, but only if it would otherwise fall
@@ -325,12 +384,18 @@
325
384
 
326
385
  if (this.singleDatePicker) {
327
386
  this.opens = 'right';
387
+ this.container.addClass('single');
328
388
  this.container.find('.calendar.right').show();
329
389
  this.container.find('.calendar.left').hide();
330
- this.container.find('.ranges').hide();
390
+ if (!this.timePicker) {
391
+ this.container.find('.ranges').hide();
392
+ } else {
393
+ this.container.find('.ranges .daterangepicker_start_input, .ranges .daterangepicker_end_input').hide();
394
+ }
331
395
  if (!this.container.find('.calendar.right').hasClass('single'))
332
396
  this.container.find('.calendar.right').addClass('single');
333
397
  } else {
398
+ this.container.removeClass('single');
334
399
  this.container.find('.calendar.right').removeClass('single');
335
400
  this.container.find('.ranges').show();
336
401
  }
@@ -340,21 +405,32 @@
340
405
  this.oldChosenLabel = this.chosenLabel;
341
406
 
342
407
  this.leftCalendar = {
343
- month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute()]),
408
+ month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute(), this.startDate.second()]),
344
409
  calendar: []
345
410
  };
346
411
 
347
412
  this.rightCalendar = {
348
- month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute()]),
413
+ month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute(), this.endDate.second()]),
349
414
  calendar: []
350
415
  };
351
416
 
352
- if (this.opens == 'right') {
417
+ if (this.opens == 'right' || this.opens == 'center') {
353
418
  //swap calendar positions
354
- var left = this.container.find('.calendar.left');
355
- var right = this.container.find('.calendar.right');
356
- left.removeClass('left').addClass('right');
357
- right.removeClass('right').addClass('left');
419
+ var first = this.container.find('.calendar.first');
420
+ var second = this.container.find('.calendar.second');
421
+
422
+ if (second.hasClass('single')) {
423
+ second.removeClass('single');
424
+ first.addClass('single');
425
+ }
426
+
427
+ first.removeClass('left').addClass('right');
428
+ second.removeClass('right').addClass('left');
429
+
430
+ if (this.singleDatePicker) {
431
+ first.show();
432
+ second.hide();
433
+ }
358
434
  }
359
435
 
360
436
  if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
@@ -370,7 +446,7 @@
370
446
 
371
447
  setStartDate: function(startDate) {
372
448
  if (typeof startDate === 'string')
373
- this.startDate = moment(startDate, this.format);
449
+ this.startDate = moment(startDate, this.format).utcOffset(this.timeZone);
374
450
 
375
451
  if (typeof startDate === 'object')
376
452
  this.startDate = moment(startDate);
@@ -382,11 +458,12 @@
382
458
 
383
459
  this.updateView();
384
460
  this.updateCalendars();
461
+ this.updateInputText();
385
462
  },
386
463
 
387
464
  setEndDate: function(endDate) {
388
465
  if (typeof endDate === 'string')
389
- this.endDate = moment(endDate, this.format);
466
+ this.endDate = moment(endDate, this.format).utcOffset(this.timeZone);
390
467
 
391
468
  if (typeof endDate === 'object')
392
469
  this.endDate = moment(endDate);
@@ -398,11 +475,12 @@
398
475
 
399
476
  this.updateView();
400
477
  this.updateCalendars();
478
+ this.updateInputText();
401
479
  },
402
480
 
403
481
  updateView: function () {
404
- this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
405
- this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
482
+ this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
483
+ this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
406
484
  this.updateFormInputs();
407
485
  },
408
486
 
@@ -426,12 +504,12 @@
426
504
  end = null;
427
505
 
428
506
  if(dateString.length === 2) {
429
- start = moment(dateString[0], this.format);
430
- end = moment(dateString[1], this.format);
507
+ start = moment(dateString[0], this.format).utcOffset(this.timeZone);
508
+ end = moment(dateString[1], this.format).utcOffset(this.timeZone);
431
509
  }
432
510
 
433
511
  if (this.singleDatePicker || start === null || end === null) {
434
- start = moment(this.element.val(), this.format);
512
+ start = moment(this.element.val(), this.format).utcOffset(this.timeZone);
435
513
  end = start;
436
514
  }
437
515
 
@@ -456,17 +534,19 @@
456
534
 
457
535
  move: function () {
458
536
  var parentOffset = { top: 0, left: 0 };
537
+ var parentRightEdge = $(window).width();
459
538
  if (!this.parentEl.is('body')) {
460
539
  parentOffset = {
461
540
  top: this.parentEl.offset().top - this.parentEl.scrollTop(),
462
541
  left: this.parentEl.offset().left - this.parentEl.scrollLeft()
463
542
  };
543
+ parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
464
544
  }
465
545
 
466
546
  if (this.opens == 'left') {
467
547
  this.container.css({
468
548
  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
469
- right: $(window).width() - this.element.offset().left - this.element.outerWidth() - parentOffset.left,
549
+ right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),
470
550
  left: 'auto'
471
551
  });
472
552
  if (this.container.offset().left < 0) {
@@ -475,6 +555,19 @@
475
555
  left: 9
476
556
  });
477
557
  }
558
+ } else if (this.opens == 'center') {
559
+ this.container.css({
560
+ top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
561
+ left: this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
562
+ - this.container.outerWidth() / 2,
563
+ right: 'auto'
564
+ });
565
+ if (this.container.offset().left < 0) {
566
+ this.container.css({
567
+ right: 'auto',
568
+ left: 9
569
+ });
570
+ }
478
571
  } else {
479
572
  this.container.css({
480
573
  top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
@@ -499,6 +592,8 @@
499
592
  },
500
593
 
501
594
  show: function (e) {
595
+ if (this.isShowing) return;
596
+
502
597
  this.element.addClass('active');
503
598
  this.container.show();
504
599
  this.move();
@@ -508,11 +603,14 @@
508
603
  // Bind global datepicker mousedown for hiding and
509
604
  $(document)
510
605
  .on('mousedown.daterangepicker', this._outsideClickProxy)
606
+ // also support mobile devices
607
+ .on('touchend.daterangepicker', this._outsideClickProxy)
511
608
  // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
512
609
  .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
513
610
  // and also close when focus changes to outside the picker (eg. tabbing between controls)
514
611
  .on('focusin.daterangepicker', this._outsideClickProxy);
515
612
 
613
+ this.isShowing = true;
516
614
  this.element.trigger('show.daterangepicker', this);
517
615
  },
518
616
 
@@ -521,6 +619,8 @@
521
619
  // if the page is clicked anywhere except within the daterangerpicker/button
522
620
  // itself then call this.hide()
523
621
  if (
622
+ // ie modal dialog fix
623
+ e.type == "focusin" ||
524
624
  target.closest(this.element).length ||
525
625
  target.closest(this.container).length ||
526
626
  target.closest('.calendar-date').length
@@ -529,10 +629,10 @@
529
629
  },
530
630
 
531
631
  hide: function (e) {
632
+ if (!this.isShowing) return;
633
+
532
634
  $(document)
533
- .off('mousedown.daterangepicker', this._outsideClickProxy)
534
- .off('click.daterangepicker', this._outsideClickProxy)
535
- .off('focusin.daterangepicker', this._outsideClickProxy);
635
+ .off('.daterangepicker');
536
636
 
537
637
  this.element.removeClass('active');
538
638
  this.container.hide();
@@ -543,6 +643,7 @@
543
643
  this.oldStartDate = this.startDate.clone();
544
644
  this.oldEndDate = this.endDate.clone();
545
645
 
646
+ this.isShowing = false;
546
647
  this.element.trigger('hide.daterangepicker', this);
547
648
  },
548
649
 
@@ -561,17 +662,45 @@
561
662
  showCalendars: function() {
562
663
  this.container.addClass('show-calendar');
563
664
  this.move();
665
+ this.element.trigger('showCalendar.daterangepicker', this);
564
666
  },
565
667
 
566
668
  hideCalendars: function() {
567
669
  this.container.removeClass('show-calendar');
670
+ this.element.trigger('hideCalendar.daterangepicker', this);
671
+ },
672
+
673
+ // when a date is typed into the start to end date textboxes
674
+ inputsChanged: function (e) {
675
+ var el = $(e.target);
676
+ var date = moment(el.val(), this.format);
677
+ if (!date.isValid()) return;
678
+
679
+ var startDate, endDate;
680
+ if (el.attr('name') === 'daterangepicker_start') {
681
+ startDate = (false !== this.minDate && date.isBefore(this.minDate)) ? this.minDate : date;
682
+ endDate = this.endDate;
683
+ } else {
684
+ startDate = this.startDate;
685
+ endDate = (false !== this.maxDate && date.isAfter(this.maxDate)) ? this.maxDate : date;
686
+ }
687
+ this.setCustomDates(startDate, endDate);
688
+ },
689
+
690
+ inputsKeydown: function(e) {
691
+ if (e.keyCode === 13) {
692
+ this.inputsChanged(e);
693
+ this.notify();
694
+ }
568
695
  },
569
696
 
570
697
  updateInputText: function() {
571
698
  if (this.element.is('input') && !this.singleDatePicker) {
572
699
  this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
700
+ this.element.trigger('change');
573
701
  } else if (this.element.is('input')) {
574
- this.element.val(this.startDate.format(this.format));
702
+ this.element.val(this.endDate.format(this.format));
703
+ this.element.trigger('change');
575
704
  }
576
705
  },
577
706
 
@@ -606,9 +735,9 @@
606
735
  clickPrev: function (e) {
607
736
  var cal = $(e.target).parents('.calendar');
608
737
  if (cal.hasClass('left')) {
609
- this.leftCalendar.month.subtract('month', 1);
738
+ this.leftCalendar.month.subtract(1, 'month');
610
739
  } else {
611
- this.rightCalendar.month.subtract('month', 1);
740
+ this.rightCalendar.month.subtract(1, 'month');
612
741
  }
613
742
  this.updateCalendars();
614
743
  },
@@ -616,15 +745,14 @@
616
745
  clickNext: function (e) {
617
746
  var cal = $(e.target).parents('.calendar');
618
747
  if (cal.hasClass('left')) {
619
- this.leftCalendar.month.add('month', 1);
748
+ this.leftCalendar.month.add(1, 'month');
620
749
  } else {
621
- this.rightCalendar.month.add('month', 1);
750
+ this.rightCalendar.month.add(1, 'month');
622
751
  }
623
752
  this.updateCalendars();
624
753
  },
625
754
 
626
- enterDate: function (e) {
627
-
755
+ hoverDate: function (e) {
628
756
  var title = $(e.target).attr('data-title');
629
757
  var row = title.substr(1, 1);
630
758
  var col = title.substr(3, 1);
@@ -635,7 +763,22 @@
635
763
  } else {
636
764
  this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].format(this.format));
637
765
  }
766
+ },
767
+
768
+ setCustomDates: function(startDate, endDate) {
769
+ this.chosenLabel = this.locale.customRangeLabel;
770
+ if (startDate.isAfter(endDate)) {
771
+ var difference = this.endDate.diff(this.startDate);
772
+ endDate = moment(startDate).add(difference, 'ms');
773
+ if (this.maxDate && endDate.isAfter(this.maxDate)) {
774
+ endDate = this.maxDate;
775
+ }
776
+ }
777
+ this.startDate = startDate;
778
+ this.endDate = endDate;
638
779
 
780
+ this.updateView();
781
+ this.updateCalendars();
639
782
  },
640
783
 
641
784
  clickDate: function (e) {
@@ -673,27 +816,14 @@
673
816
 
674
817
  cal.find('td').removeClass('active');
675
818
 
676
- if (startDate.isSame(endDate) || startDate.isBefore(endDate)) {
677
- $(e.target).addClass('active');
678
- this.startDate = startDate;
679
- this.endDate = endDate;
680
- this.chosenLabel = this.locale.customRangeLabel;
681
- } else if (startDate.isAfter(endDate)) {
682
- $(e.target).addClass('active');
683
- var difference = this.endDate.diff(this.startDate);
684
- this.startDate = startDate;
685
- this.endDate = moment(startDate).add('ms', difference);
686
- this.chosenLabel = this.locale.customRangeLabel;
687
- }
819
+ $(e.target).addClass('active');
688
820
 
689
- this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year());
690
- this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year());
691
- this.updateCalendars();
821
+ this.setCustomDates(startDate, endDate);
692
822
 
693
823
  if (!this.timePicker)
694
824
  endDate.endOf('day');
695
825
 
696
- if (this.singleDatePicker)
826
+ if (this.singleDatePicker && !this.timePicker)
697
827
  this.clickApply();
698
828
  },
699
829
 
@@ -722,6 +852,28 @@
722
852
  var month = parseInt(cal.find('.monthselect').val(), 10);
723
853
  var year = cal.find('.yearselect').val();
724
854
 
855
+ if (!isLeft && !this.singleDatePicker) {
856
+ if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {
857
+ month = this.startDate.month();
858
+ year = this.startDate.year();
859
+ }
860
+ }
861
+
862
+ if (this.minDate) {
863
+ if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {
864
+ month = this.minDate.month();
865
+ year = this.minDate.year();
866
+ }
867
+ }
868
+
869
+ if (this.maxDate) {
870
+ if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {
871
+ month = this.maxDate.month();
872
+ year = this.maxDate.year();
873
+ }
874
+ }
875
+
876
+
725
877
  this[leftOrRight+'Calendar'].month.month(month).year(year);
726
878
  this.updateCalendars();
727
879
  },
@@ -733,6 +885,11 @@
733
885
 
734
886
  var hour = parseInt(cal.find('.hourselect').val(), 10);
735
887
  var minute = parseInt(cal.find('.minuteselect').val(), 10);
888
+ var second = 0;
889
+
890
+ if (this.timePickerSeconds) {
891
+ second = parseInt(cal.find('.secondselect').val(), 10);
892
+ }
736
893
 
737
894
  if (this.timePicker12Hour) {
738
895
  var ampm = cal.find('.ampmselect').val();
@@ -746,24 +903,31 @@
746
903
  var start = this.startDate.clone();
747
904
  start.hour(hour);
748
905
  start.minute(minute);
906
+ start.second(second);
749
907
  this.startDate = start;
750
- this.leftCalendar.month.hour(hour).minute(minute);
908
+ this.leftCalendar.month.hour(hour).minute(minute).second(second);
909
+ if (this.singleDatePicker)
910
+ this.endDate = start.clone();
751
911
  } else {
752
912
  var end = this.endDate.clone();
753
913
  end.hour(hour);
754
914
  end.minute(minute);
915
+ end.second(second);
755
916
  this.endDate = end;
756
- this.rightCalendar.month.hour(hour).minute(minute);
917
+ if (this.singleDatePicker)
918
+ this.startDate = end.clone();
919
+ this.rightCalendar.month.hour(hour).minute(minute).second(second);
757
920
  }
758
921
 
922
+ this.updateView();
759
923
  this.updateCalendars();
760
924
  },
761
925
 
762
926
  updateCalendars: function () {
763
- this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.month(), this.leftCalendar.month.year(), this.leftCalendar.month.hour(), this.leftCalendar.month.minute(), 'left');
764
- this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.month(), this.rightCalendar.month.year(), this.rightCalendar.month.hour(), this.rightCalendar.month.minute(), 'right');
765
- this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate));
766
- this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.startDate, this.maxDate));
927
+ 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');
928
+ 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');
929
+ this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate, 'left'));
930
+ this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.singleDatePicker ? this.minDate : this.startDate, this.maxDate, 'right'));
767
931
 
768
932
  this.container.find('.ranges li').removeClass('active');
769
933
  var customRange = true;
@@ -786,15 +950,17 @@
786
950
  i++;
787
951
  }
788
952
  if (customRange) {
789
- this.chosenLabel = this.container.find('.ranges li:last')
790
- .addClass('active').html();
953
+ this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
954
+ this.showCalendars();
791
955
  }
792
956
  },
793
957
 
794
- buildCalendar: function (month, year, hour, minute, side) {
958
+ buildCalendar: function (month, year, hour, minute, second, side) {
959
+ var daysInMonth = moment([year, month]).daysInMonth();
795
960
  var firstDay = moment([year, month, 1]);
796
- var lastMonth = moment(firstDay).subtract('month', 1).month();
797
- var lastYear = moment(firstDay).subtract('month', 1).year();
961
+ var lastDay = moment([year, month, daysInMonth]);
962
+ var lastMonth = moment(firstDay).subtract(1, 'month').month();
963
+ var lastYear = moment(firstDay).subtract(1, 'month').year();
798
964
 
799
965
  var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
800
966
 
@@ -804,6 +970,9 @@
804
970
 
805
971
  //initialize a 6 rows x 7 columns array for the calendar
806
972
  var calendar = [];
973
+ calendar.firstDay = firstDay;
974
+ calendar.lastDay = lastDay;
975
+
807
976
  for (i = 0; i < 6; i++) {
808
977
  calendar[i] = [];
809
978
  }
@@ -816,15 +985,25 @@
816
985
  if (dayOfWeek == this.locale.firstDay)
817
986
  startDay = daysInLastMonth - 6;
818
987
 
819
- var curDate = moment([lastYear, lastMonth, startDay, 12, minute]);
988
+ var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]).utcOffset(this.timeZone);
989
+
820
990
  var col, row;
821
- for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add('hour', 24)) {
991
+ for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
822
992
  if (i > 0 && col % 7 === 0) {
823
993
  col = 0;
824
994
  row++;
825
995
  }
826
996
  calendar[row][col] = curDate.clone().hour(hour);
827
997
  curDate.hour(12);
998
+
999
+ if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
1000
+ calendar[row][col] = this.minDate.clone();
1001
+ }
1002
+
1003
+ if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
1004
+ calendar[row][col] = this.maxDate.clone();
1005
+ }
1006
+
828
1007
  }
829
1008
 
830
1009
  return calendar;
@@ -832,9 +1011,13 @@
832
1011
 
833
1012
  renderDropdowns: function (selected, minDate, maxDate) {
834
1013
  var currentMonth = selected.month();
1014
+ var currentYear = selected.year();
1015
+ var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
1016
+ var minYear = (minDate && minDate.year()) || (currentYear - 50);
1017
+
835
1018
  var monthHtml = '<select class="monthselect">';
836
- var inMinYear = false;
837
- var inMaxYear = false;
1019
+ var inMinYear = currentYear == minYear;
1020
+ var inMaxYear = currentYear == maxYear;
838
1021
 
839
1022
  for (var m = 0; m < 12; m++) {
840
1023
  if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
@@ -845,9 +1028,6 @@
845
1028
  }
846
1029
  monthHtml += "</select>";
847
1030
 
848
- var currentYear = selected.year();
849
- var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
850
- var minYear = (minDate && minDate.year()) || (currentYear - 50);
851
1031
  var yearHtml = '<select class="yearselect">';
852
1032
 
853
1033
  for (var y = minYear; y <= maxYear; y++) {
@@ -861,7 +1041,7 @@
861
1041
  return monthHtml + yearHtml;
862
1042
  },
863
1043
 
864
- renderCalendar: function (calendar, selected, minDate, maxDate) {
1044
+ renderCalendar: function (calendar, selected, minDate, maxDate, side) {
865
1045
 
866
1046
  var html = '<div class="calendar-date">';
867
1047
  html += '<table class="table-condensed">';
@@ -872,8 +1052,8 @@
872
1052
  if (this.showWeekNumbers)
873
1053
  html += '<th></th>';
874
1054
 
875
- if (!minDate || minDate.isBefore(calendar[1][1])) {
876
- html += '<th class="prev available"><i class="fa fa-arrow-left icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
1055
+ if (!minDate || minDate.isBefore(calendar.firstDay)) {
1056
+ html += '<th class="prev available"><i class="fa fa-arrow-left icon icon-arrow-left glyphicon glyphicon-arrow-left"></i></th>';
877
1057
  } else {
878
1058
  html += '<th></th>';
879
1059
  }
@@ -885,8 +1065,8 @@
885
1065
  }
886
1066
 
887
1067
  html += '<th colspan="5" class="month">' + dateHtml + '</th>';
888
- if (!maxDate || maxDate.isAfter(calendar[1][1])) {
889
- html += '<th class="next available"><i class="fa fa-arrow-right icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
1068
+ if (!maxDate || maxDate.isAfter(calendar.lastDay)) {
1069
+ html += '<th class="next available"><i class="fa fa-arrow-right icon icon-arrow-right glyphicon glyphicon-arrow-right"></i></th>';
890
1070
  } else {
891
1071
  html += '<th></th>';
892
1072
  }
@@ -948,6 +1128,29 @@
948
1128
 
949
1129
  html += '<div class="calendar-time">';
950
1130
  html += '<select class="hourselect">';
1131
+
1132
+ // Disallow selections before the minDate or after the maxDate
1133
+ var min_hour = 0;
1134
+ var max_hour = 23;
1135
+
1136
+ if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == minDate.format('YYYY-MM-DD')) {
1137
+ min_hour = minDate.hour();
1138
+ if (selected.hour() < min_hour)
1139
+ selected.hour(min_hour);
1140
+ if (this.timePicker12Hour && min_hour >= 12 && selected.hour() >= 12)
1141
+ min_hour -= 12;
1142
+ if (this.timePicker12Hour && min_hour == 12)
1143
+ min_hour = 1;
1144
+ }
1145
+
1146
+ if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == maxDate.format('YYYY-MM-DD')) {
1147
+ max_hour = maxDate.hour();
1148
+ if (selected.hour() > max_hour)
1149
+ selected.hour(max_hour);
1150
+ if (this.timePicker12Hour && max_hour >= 12 && selected.hour() >= 12)
1151
+ max_hour -= 12;
1152
+ }
1153
+
951
1154
  var start = 0;
952
1155
  var end = 23;
953
1156
  var selected_hour = selected.hour();
@@ -961,8 +1164,11 @@
961
1164
  }
962
1165
 
963
1166
  for (i = start; i <= end; i++) {
1167
+
964
1168
  if (i == selected_hour) {
965
1169
  html += '<option value="' + i + '" selected="selected">' + i + '</option>';
1170
+ } else if (i < min_hour || i > max_hour) {
1171
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>';
966
1172
  } else {
967
1173
  html += '<option value="' + i + '">' + i + '</option>';
968
1174
  }
@@ -972,12 +1178,30 @@
972
1178
 
973
1179
  html += '<select class="minuteselect">';
974
1180
 
1181
+ // Disallow selections before the minDate or after the maxDate
1182
+ var min_minute = 0;
1183
+ var max_minute = 59;
1184
+
1185
+ if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD h A') == minDate.format('YYYY-MM-DD h A')) {
1186
+ min_minute = minDate.minute();
1187
+ if (selected.minute() < min_minute)
1188
+ selected.minute(min_minute);
1189
+ }
1190
+
1191
+ if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD h A') == maxDate.format('YYYY-MM-DD h A')) {
1192
+ max_minute = maxDate.minute();
1193
+ if (selected.minute() > max_minute)
1194
+ selected.minute(max_minute);
1195
+ }
1196
+
975
1197
  for (i = 0; i < 60; i += this.timePickerIncrement) {
976
1198
  var num = i;
977
1199
  if (num < 10)
978
1200
  num = '0' + num;
979
1201
  if (i == selected.minute()) {
980
1202
  html += '<option value="' + i + '" selected="selected">' + num + '</option>';
1203
+ } else if (i < min_minute || i > max_minute) {
1204
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + num + '</option>';
981
1205
  } else {
982
1206
  html += '<option value="' + i + '">' + num + '</option>';
983
1207
  }
@@ -985,12 +1209,42 @@
985
1209
 
986
1210
  html += '</select> ';
987
1211
 
1212
+ if (this.timePickerSeconds) {
1213
+ html += ': <select class="secondselect">';
1214
+
1215
+ for (i = 0; i < 60; i += this.timePickerIncrement) {
1216
+ var num = i;
1217
+ if (num < 10)
1218
+ num = '0' + num;
1219
+ if (i == selected.second()) {
1220
+ html += '<option value="' + i + '" selected="selected">' + num + '</option>';
1221
+ } else {
1222
+ html += '<option value="' + i + '">' + num + '</option>';
1223
+ }
1224
+ }
1225
+
1226
+ html += '</select>';
1227
+ }
1228
+
988
1229
  if (this.timePicker12Hour) {
989
1230
  html += '<select class="ampmselect">';
1231
+
1232
+ // Disallow selection before the minDate or after the maxDate
1233
+ var am_html = '';
1234
+ var pm_html = '';
1235
+
1236
+ if (minDate && (side == 'left' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == minDate.format('YYYY-MM-DD') && minDate.hour() >= 12) {
1237
+ am_html = ' disabled="disabled" class="disabled"';
1238
+ }
1239
+
1240
+ if (maxDate && (side == 'right' || this.singleDatePicker) && selected.format('YYYY-MM-DD') == maxDate.format('YYYY-MM-DD') && maxDate.hour() < 12) {
1241
+ pm_html = ' disabled="disabled" class="disabled"';
1242
+ }
1243
+
990
1244
  if (selected.hour() >= 12) {
991
- html += '<option value="AM">AM</option><option value="PM" selected="selected">PM</option>';
1245
+ html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>';
992
1246
  } else {
993
- html += '<option value="AM" selected="selected">AM</option><option value="PM">PM</option>';
1247
+ html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>';
994
1248
  }
995
1249
  html += '</select>';
996
1250
  }
@@ -1023,4 +1277,4 @@
1023
1277
  return this;
1024
1278
  };
1025
1279
 
1026
- }(window.jQuery, window.moment);
1280
+ }));