github.com/apremalal/vamps-core@v1.0.1-0.20161221121535-d430b56ec174/server/webapps/app/base/plugins/bootstrap-daterangepicker/daterangepicker.js (about)

     1  /**
     2  * @version: 1.3.13
     3  * @author: Dan Grossman http://www.dangrossman.info/
     4  * @date: 2014-09-04
     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/
     8  */
     9  
    10  (function(root, factory) {
    11  
    12    if (typeof define === 'function' && define.amd) {
    13      define(['moment', 'jquery', 'exports'], function(momentjs, $, exports) {
    14        root.daterangepicker = factory(root, exports, momentjs, $);
    15      });
    16  
    17    } else if (typeof exports !== 'undefined') {
    18      var momentjs = require('moment');
    19      var jQuery;
    20      try {
    21        jQuery = require('jquery');
    22      } catch (err) {
    23        jQuery = window.jQuery;
    24        if (!jQuery) throw new Error('jQuery dependency not found');
    25      }
    26  
    27      factory(root, exports, momentjs, jQuery);
    28  
    29    // Finally, as a browser global.
    30    } else {
    31      root.daterangepicker = factory(root, {}, root.moment, (root.jQuery || root.Zepto || root.ender || root.$));
    32    }
    33  
    34  }(this, function(root, daterangepicker, moment, $) {
    35  
    36      var DateRangePicker = function (element, options, cb) {
    37  
    38          // by default, the daterangepicker element is placed at the bottom of HTML body
    39          this.parentEl = 'body';
    40  
    41          //element that triggered the date range picker
    42          this.element = $(element);
    43  
    44          //tracks visible state
    45          this.isShowing = false;
    46  
    47          //create the picker HTML object
    48          var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
    49                  '<div class="calendar left"></div>' +
    50                  '<div class="calendar right"></div>' +
    51                  '<div class="ranges">' +
    52                    '<div class="range_inputs">' +
    53                      '<div class="daterangepicker_start_input">' +
    54                        '<label for="daterangepicker_start"></label>' +
    55                        '<input class="input-mini" type="text" name="daterangepicker_start" value="" />' +
    56                      '</div>' +
    57                      '<div class="daterangepicker_end_input">' +
    58                        '<label for="daterangepicker_end"></label>' +
    59                        '<input class="input-mini" type="text" name="daterangepicker_end" value="" />' +
    60                      '</div>' +
    61                      '<button class="applyBtn" disabled="disabled"></button>&nbsp;' +
    62                      '<button class="cancelBtn"></button>' +
    63                    '</div>' +
    64                  '</div>' +
    65                '</div>';
    66  
    67          //custom options
    68          if (typeof options !== 'object' || options === null)
    69              options = {};
    70  
    71          this.parentEl = (typeof options === 'object' && options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
    72          this.container = $(DRPTemplate).appendTo(this.parentEl);
    73  
    74          this.setOptions(options, cb);
    75  
    76          //apply CSS classes and labels to buttons
    77          var c = this.container;
    78          $.each(this.buttonClasses, function (idx, val) {
    79              c.find('button').addClass(val);
    80          });
    81          this.container.find('.daterangepicker_start_input label').html(this.locale.fromLabel);
    82          this.container.find('.daterangepicker_end_input label').html(this.locale.toLabel);
    83          if (this.applyClass.length)
    84              this.container.find('.applyBtn').addClass(this.applyClass);
    85          if (this.cancelClass.length)
    86              this.container.find('.cancelBtn').addClass(this.cancelClass);
    87          this.container.find('.applyBtn').html(this.locale.applyLabel);
    88          this.container.find('.cancelBtn').html(this.locale.cancelLabel);
    89  
    90          //event listeners
    91  
    92          this.container.find('.calendar')
    93              .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
    94              .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
    95              .on('click.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
    96              .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
    97              .on('mouseleave.daterangepicker', 'td.available', $.proxy(this.updateFormInputs, this))
    98              .on('change.daterangepicker', 'select.yearselect', $.proxy(this.updateMonthYear, this))
    99              .on('change.daterangepicker', 'select.monthselect', $.proxy(this.updateMonthYear, this))
   100              .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.ampmselect', $.proxy(this.updateTime, this));
   101  
   102          this.container.find('.ranges')
   103              .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
   104              .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
   105              .on('click.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.showCalendars, this))
   106              .on('change.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsChanged, this))
   107              .on('keydown.daterangepicker', '.daterangepicker_start_input,.daterangepicker_end_input', $.proxy(this.inputsKeydown, this))
   108              .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
   109              .on('mouseenter.daterangepicker', 'li', $.proxy(this.enterRange, this))
   110              .on('mouseleave.daterangepicker', 'li', $.proxy(this.updateFormInputs, this));
   111  
   112          if (this.element.is('input')) {
   113              this.element.on({
   114                  'click.daterangepicker': $.proxy(this.show, this),
   115                  'focus.daterangepicker': $.proxy(this.show, this),
   116                  'keyup.daterangepicker': $.proxy(this.updateFromControl, this)
   117              });
   118          } else {
   119              this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
   120          }
   121  
   122      };
   123  
   124      DateRangePicker.prototype = {
   125  
   126          constructor: DateRangePicker,
   127  
   128          setOptions: function(options, callback) {
   129  
   130              this.startDate = moment().startOf('day');
   131              this.endDate = moment().endOf('day');
   132              this.minDate = false;
   133              this.maxDate = false;
   134              this.dateLimit = false;
   135  
   136              this.showDropdowns = false;
   137              this.showWeekNumbers = false;
   138              this.timePicker = false;
   139              this.timePickerIncrement = 30;
   140              this.timePicker12Hour = true;
   141              this.singleDatePicker = false;
   142              this.ranges = {};
   143  
   144              this.opens = 'right';
   145              if (this.element.hasClass('pull-right'))
   146                  this.opens = 'left';
   147  
   148              this.buttonClasses = ['btn', 'btn-small btn-sm'];
   149              this.applyClass = 'btn-success';
   150              this.cancelClass = 'btn-default';
   151  
   152              this.format = 'MM/DD/YYYY';
   153              this.separator = ' - ';
   154  
   155              this.locale = {
   156                  applyLabel: 'Apply',
   157                  cancelLabel: 'Cancel',
   158                  fromLabel: 'From',
   159                  toLabel: 'To',
   160                  weekLabel: 'W',
   161                  customRangeLabel: 'Custom Range',
   162                  daysOfWeek: moment.weekdaysMin(),
   163                  monthNames: moment.monthsShort(),
   164                  firstDay: moment.localeData()._week.dow
   165              };
   166  
   167              this.cb = function () { };
   168  
   169              if (typeof options.format === 'string')
   170                  this.format = options.format;
   171  
   172              if (typeof options.separator === 'string')
   173                  this.separator = options.separator;
   174  
   175              if (typeof options.startDate === 'string')
   176                  this.startDate = moment(options.startDate, this.format);
   177  
   178              if (typeof options.endDate === 'string')
   179                  this.endDate = moment(options.endDate, this.format);
   180  
   181              if (typeof options.minDate === 'string')
   182                  this.minDate = moment(options.minDate, this.format);
   183  
   184              if (typeof options.maxDate === 'string')
   185                  this.maxDate = moment(options.maxDate, this.format);
   186  
   187              if (typeof options.startDate === 'object')
   188                  this.startDate = moment(options.startDate);
   189  
   190              if (typeof options.endDate === 'object')
   191                  this.endDate = moment(options.endDate);
   192  
   193              if (typeof options.minDate === 'object')
   194                  this.minDate = moment(options.minDate);
   195  
   196              if (typeof options.maxDate === 'object')
   197                  this.maxDate = moment(options.maxDate);
   198  
   199              if (typeof options.applyClass === 'string')
   200                  this.applyClass = options.applyClass;
   201  
   202              if (typeof options.cancelClass === 'string')
   203                  this.cancelClass = options.cancelClass;
   204  
   205              if (typeof options.dateLimit === 'object')
   206                  this.dateLimit = options.dateLimit;
   207  
   208              if (typeof options.locale === 'object') {
   209  
   210                  if (typeof options.locale.daysOfWeek === 'object') {
   211                      // Create a copy of daysOfWeek to avoid modification of original
   212                      // options object for reusability in multiple daterangepicker instances
   213                      this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
   214                  }
   215  
   216                  if (typeof options.locale.monthNames === 'object') {
   217                    this.locale.monthNames = options.locale.monthNames.slice();
   218                  }
   219  
   220                  if (typeof options.locale.firstDay === 'number') {
   221                    this.locale.firstDay = options.locale.firstDay;
   222                  }
   223  
   224                  if (typeof options.locale.applyLabel === 'string') {
   225                    this.locale.applyLabel = options.locale.applyLabel;
   226                  }
   227  
   228                  if (typeof options.locale.cancelLabel === 'string') {
   229                    this.locale.cancelLabel = options.locale.cancelLabel;
   230                  }
   231  
   232                  if (typeof options.locale.fromLabel === 'string') {
   233                    this.locale.fromLabel = options.locale.fromLabel;
   234                  }
   235  
   236                  if (typeof options.locale.toLabel === 'string') {
   237                    this.locale.toLabel = options.locale.toLabel;
   238                  }
   239  
   240                  if (typeof options.locale.weekLabel === 'string') {
   241                    this.locale.weekLabel = options.locale.weekLabel;
   242                  }
   243  
   244                  if (typeof options.locale.customRangeLabel === 'string') {
   245                    this.locale.customRangeLabel = options.locale.customRangeLabel;
   246                  }
   247              }
   248  
   249              if (typeof options.opens === 'string')
   250                  this.opens = options.opens;
   251  
   252              if (typeof options.showWeekNumbers === 'boolean') {
   253                  this.showWeekNumbers = options.showWeekNumbers;
   254              }
   255  
   256              if (typeof options.buttonClasses === 'string') {
   257                  this.buttonClasses = [options.buttonClasses];
   258              }
   259  
   260              if (typeof options.buttonClasses === 'object') {
   261                  this.buttonClasses = options.buttonClasses;
   262              }
   263  
   264              if (typeof options.showDropdowns === 'boolean') {
   265                  this.showDropdowns = options.showDropdowns;
   266              }
   267  
   268              if (typeof options.singleDatePicker === 'boolean') {
   269                  this.singleDatePicker = options.singleDatePicker;
   270                  if (this.singleDatePicker) {
   271                      this.endDate = this.startDate.clone();
   272                  }
   273              }
   274  
   275              if (typeof options.timePicker === 'boolean') {
   276                  this.timePicker = options.timePicker;
   277              }
   278  
   279              if (typeof options.timePickerIncrement === 'number') {
   280                  this.timePickerIncrement = options.timePickerIncrement;
   281              }
   282  
   283              if (typeof options.timePicker12Hour === 'boolean') {
   284                  this.timePicker12Hour = options.timePicker12Hour;
   285              }
   286  
   287              // update day names order to firstDay
   288              if (this.locale.firstDay != 0) {
   289                  var iterator = this.locale.firstDay;
   290                  while (iterator > 0) {
   291                      this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
   292                      iterator--;
   293                  }
   294              }
   295  
   296              var start, end, range;
   297  
   298              //if no start/end dates set, check if an input element contains initial values
   299              if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
   300                  if ($(this.element).is('input[type=text]')) {
   301                      var val = $(this.element).val();
   302                      var split = val.split(this.separator);
   303                      start = end = null;
   304                      if (split.length == 2) {
   305                          start = moment(split[0], this.format);
   306                          end = moment(split[1], this.format);
   307                      } else if (this.singleDatePicker) {
   308                          start = moment(val, this.format);
   309                          end = moment(val, this.format);
   310                      }
   311                      if (start !== null && end !== null) {
   312                          this.startDate = start;
   313                          this.endDate = end;
   314                      }
   315                  }
   316              }
   317  
   318              if (typeof options.ranges === 'object') {
   319                  for (range in options.ranges) {
   320  
   321                      start = moment(options.ranges[range][0]);
   322                      end = moment(options.ranges[range][1]);
   323  
   324                      // If we have a min/max date set, bound this range
   325                      // to it, but only if it would otherwise fall
   326                      // outside of the min/max.
   327                      if (this.minDate && start.isBefore(this.minDate))
   328                          start = moment(this.minDate);
   329  
   330                      if (this.maxDate && end.isAfter(this.maxDate))
   331                          end = moment(this.maxDate);
   332  
   333                      // If the end of the range is before the minimum (if min is set) OR
   334                      // the start of the range is after the max (also if set) don't display this
   335                      // range option.
   336                      if ((this.minDate && end.isBefore(this.minDate)) || (this.maxDate && start.isAfter(this.maxDate))) {
   337                          continue;
   338                      }
   339  
   340                      this.ranges[range] = [start, end];
   341                  }
   342  
   343                  var list = '<ul>';
   344                  for (range in this.ranges) {
   345                      list += '<li>' + range + '</li>';
   346                  }
   347                  list += '<li>' + this.locale.customRangeLabel + '</li>';
   348                  list += '</ul>';
   349                  this.container.find('.ranges ul').remove();
   350                  this.container.find('.ranges').prepend(list);
   351              }
   352  
   353              if (typeof callback === 'function') {
   354                  this.cb = callback;
   355              }
   356  
   357              if (!this.timePicker) {
   358                  this.startDate = this.startDate.startOf('day');
   359                  this.endDate = this.endDate.endOf('day');
   360              }
   361  
   362              if (this.singleDatePicker) {
   363              	this.opens = 'right';
   364              	this.container.addClass('single');
   365                  this.container.find('.calendar.right').show();
   366                  this.container.find('.calendar.left').hide();
   367                  if (!this.timePicker) {
   368  	                this.container.find('.ranges').hide();
   369                  } else {
   370                  	this.container.find('.ranges .daterangepicker_start_input, .ranges .daterangepicker_end_input').hide();
   371                  }
   372                  if (!this.container.find('.calendar.right').hasClass('single'))
   373                      this.container.find('.calendar.right').addClass('single');
   374              } else {
   375              	this.container.removeClass('single');
   376              	this.container.find('.calendar.right').removeClass('single');
   377                  this.container.find('.ranges').show();
   378              }
   379  
   380              this.oldStartDate = this.startDate.clone();
   381              this.oldEndDate = this.endDate.clone();
   382              this.oldChosenLabel = this.chosenLabel;
   383  
   384              this.leftCalendar = {
   385                  month: moment([this.startDate.year(), this.startDate.month(), 1, this.startDate.hour(), this.startDate.minute()]),
   386                  calendar: []
   387              };
   388  
   389              this.rightCalendar = {
   390                  month: moment([this.endDate.year(), this.endDate.month(), 1, this.endDate.hour(), this.endDate.minute()]),
   391                  calendar: []
   392              };
   393  
   394              if (this.opens == 'right') {
   395                  //swap calendar positions
   396                  var left = this.container.find('.calendar.left');
   397                  var right = this.container.find('.calendar.right');
   398  
   399                  if (right.hasClass('single')) {
   400                      right.removeClass('single');
   401                      left.addClass('single');
   402                  }
   403  
   404                  left.removeClass('left').addClass('right');
   405                  right.removeClass('right').addClass('left');
   406  
   407                  if (this.singleDatePicker) {
   408                      left.show();
   409                      right.hide();
   410                  }
   411              }
   412  
   413              if (typeof options.ranges === 'undefined' && !this.singleDatePicker) {
   414                  this.container.addClass('show-calendar');
   415              }
   416  
   417              this.container.addClass('opens' + this.opens);
   418  
   419              this.updateView();
   420              this.updateCalendars();
   421  
   422          },
   423  
   424          setStartDate: function(startDate) {
   425              if (typeof startDate === 'string')
   426                  this.startDate = moment(startDate, this.format);
   427  
   428              if (typeof startDate === 'object')
   429                  this.startDate = moment(startDate);
   430  
   431              if (!this.timePicker)
   432                  this.startDate = this.startDate.startOf('day');
   433  
   434              this.oldStartDate = this.startDate.clone();
   435  
   436              this.updateView();
   437              this.updateCalendars();
   438              this.updateInputText();
   439          },
   440  
   441          setEndDate: function(endDate) {
   442              if (typeof endDate === 'string')
   443                  this.endDate = moment(endDate, this.format);
   444  
   445              if (typeof endDate === 'object')
   446                  this.endDate = moment(endDate);
   447  
   448              if (!this.timePicker)
   449                  this.endDate = this.endDate.endOf('day');
   450  
   451              this.oldEndDate = this.endDate.clone();
   452  
   453              this.updateView();
   454              this.updateCalendars();
   455              this.updateInputText();
   456          },
   457  
   458          updateView: function () {
   459              this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
   460              this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
   461              this.updateFormInputs();
   462          },
   463  
   464          updateFormInputs: function () {
   465              this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
   466              this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
   467  
   468              if (this.startDate.isSame(this.endDate) || this.startDate.isBefore(this.endDate)) {
   469                  this.container.find('button.applyBtn').removeAttr('disabled');
   470              } else {
   471                  this.container.find('button.applyBtn').attr('disabled', 'disabled');
   472              }
   473          },
   474  
   475          updateFromControl: function () {
   476              if (!this.element.is('input')) return;
   477              if (!this.element.val().length) return;
   478  
   479              var dateString = this.element.val().split(this.separator),
   480                  start = null,
   481                  end = null;
   482  
   483              if(dateString.length === 2) {
   484                  start = moment(dateString[0], this.format);
   485                  end = moment(dateString[1], this.format);
   486              }
   487  
   488              if (this.singleDatePicker || start === null || end === null) {
   489                  start = moment(this.element.val(), this.format);
   490                  end = start;
   491              }
   492  
   493              if (end.isBefore(start)) return;
   494  
   495              this.oldStartDate = this.startDate.clone();
   496              this.oldEndDate = this.endDate.clone();
   497  
   498              this.startDate = start;
   499              this.endDate = end;
   500  
   501              if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
   502                  this.notify();
   503  
   504              this.updateCalendars();
   505          },
   506  
   507          notify: function () {
   508              this.updateView();
   509              this.cb(this.startDate, this.endDate, this.chosenLabel);
   510          },
   511  
   512          move: function () {
   513              var parentOffset = { top: 0, left: 0 };
   514              var parentRightEdge = $(window).width();
   515              if (!this.parentEl.is('body')) {
   516                  parentOffset = {
   517                      top: this.parentEl.offset().top - this.parentEl.scrollTop(),
   518                      left: this.parentEl.offset().left - this.parentEl.scrollLeft()
   519                  };
   520                  parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
   521              }
   522  
   523              if (this.opens == 'left') {
   524                  this.container.css({
   525                      top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
   526                      right: parentRightEdge - this.element.offset().left - this.element.outerWidth(),
   527                      left: 'auto'
   528                  });
   529                  if (this.container.offset().left < 0) {
   530                      this.container.css({
   531                          right: 'auto',
   532                          left: 9
   533                      });
   534                  }
   535              } else {
   536                  this.container.css({
   537                      top: this.element.offset().top + this.element.outerHeight() - parentOffset.top,
   538                      left: this.element.offset().left - parentOffset.left,
   539                      right: 'auto'
   540                  });
   541                  if (this.container.offset().left + this.container.outerWidth() > $(window).width()) {
   542                      this.container.css({
   543                          left: 'auto',
   544                          right: 0
   545                      });
   546                  }
   547              }
   548          },
   549  
   550          toggle: function (e) {
   551              if (this.element.hasClass('active')) {
   552                  this.hide();
   553              } else {
   554                  this.show();
   555              }
   556          },
   557  
   558          show: function (e) {
   559              if (this.isShowing) return;
   560  
   561              this.element.addClass('active');
   562              this.container.show();
   563              this.move();
   564  
   565              // Create a click proxy that is private to this instance of datepicker, for unbinding
   566              this._outsideClickProxy = $.proxy(function (e) { this.outsideClick(e); }, this);
   567              // Bind global datepicker mousedown for hiding and
   568              $(document)
   569                .on('mousedown.daterangepicker', this._outsideClickProxy)
   570                // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
   571                .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
   572                // and also close when focus changes to outside the picker (eg. tabbing between controls)
   573                .on('focusin.daterangepicker', this._outsideClickProxy);
   574  
   575              this.isShowing = true;
   576              this.element.trigger('show.daterangepicker', this);
   577          },
   578  
   579          outsideClick: function (e) {
   580              var target = $(e.target);
   581              // if the page is clicked anywhere except within the daterangerpicker/button
   582              // itself then call this.hide()
   583              if (
   584                  target.closest(this.element).length ||
   585                  target.closest(this.container).length ||
   586                  target.closest('.calendar-date').length
   587                  ) return;
   588              this.hide();
   589          },
   590  
   591          hide: function (e) {
   592              if (!this.isShowing) return;
   593  
   594              $(document)
   595                .off('mousedown.daterangepicker')
   596                .off('click.daterangepicker', '[data-toggle=dropdown]')
   597                .off('focusin.daterangepicker');
   598  
   599              this.element.removeClass('active');
   600              this.container.hide();
   601  
   602              if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
   603                  this.notify();
   604  
   605              this.oldStartDate = this.startDate.clone();
   606              this.oldEndDate = this.endDate.clone();
   607  
   608              this.isShowing = false;
   609              this.element.trigger('hide.daterangepicker', this);
   610          },
   611  
   612          enterRange: function (e) {
   613              // mouse pointer has entered a range label
   614              var label = e.target.innerHTML;
   615              if (label == this.locale.customRangeLabel) {
   616                  this.updateView();
   617              } else {
   618                  var dates = this.ranges[label];
   619                  this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.format));
   620                  this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.format));
   621              }
   622          },
   623  
   624          showCalendars: function() {
   625              this.container.addClass('show-calendar');
   626              this.move();
   627              this.element.trigger('showCalendar.daterangepicker', this);
   628          },
   629  
   630          hideCalendars: function() {
   631              this.container.removeClass('show-calendar');
   632              this.element.trigger('hideCalendar.daterangepicker', this);
   633          },
   634  
   635          // when a date is typed into the start to end date textboxes
   636          inputsChanged: function (e) {
   637              var el = $(e.target);
   638              var date = moment(el.val(), this.format);
   639              if (!date.isValid()) return;
   640  
   641              var startDate, endDate;
   642              if (el.attr('name') === 'daterangepicker_start') {
   643                  startDate = date;
   644                  endDate = this.endDate;
   645              } else {
   646                  startDate = this.startDate;
   647                  endDate = date;
   648              }
   649              this.setCustomDates(startDate, endDate);
   650          },
   651  
   652          inputsKeydown: function(e) {
   653              if (e.keyCode === 13) {
   654                  this.inputsChanged(e);
   655                  this.notify();
   656              }
   657          },
   658  
   659          updateInputText: function() {
   660              if (this.element.is('input') && !this.singleDatePicker) {
   661                  this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
   662              } else if (this.element.is('input')) {
   663              	this.element.val(this.endDate.format(this.format));
   664              }
   665          },
   666  
   667          clickRange: function (e) {
   668              var label = e.target.innerHTML;
   669              this.chosenLabel = label;
   670              if (label == this.locale.customRangeLabel) {
   671                  this.showCalendars();
   672              } else {
   673                  var dates = this.ranges[label];
   674  
   675                  this.startDate = dates[0];
   676                  this.endDate = dates[1];
   677  
   678                  if (!this.timePicker) {
   679                      this.startDate.startOf('day');
   680                      this.endDate.endOf('day');
   681                  }
   682  
   683                  this.leftCalendar.month.month(this.startDate.month()).year(this.startDate.year()).hour(this.startDate.hour()).minute(this.startDate.minute());
   684                  this.rightCalendar.month.month(this.endDate.month()).year(this.endDate.year()).hour(this.endDate.hour()).minute(this.endDate.minute());
   685                  this.updateCalendars();
   686  
   687                  this.updateInputText();
   688  
   689                  this.hideCalendars();
   690                  this.hide();
   691                  this.element.trigger('apply.daterangepicker', this);
   692              }
   693          },
   694  
   695          clickPrev: function (e) {
   696              var cal = $(e.target).parents('.calendar');
   697              if (cal.hasClass('left')) {
   698                  this.leftCalendar.month.subtract(1, 'month');
   699              } else {
   700                  this.rightCalendar.month.subtract(1, 'month');
   701              }
   702              this.updateCalendars();
   703          },
   704  
   705          clickNext: function (e) {
   706              var cal = $(e.target).parents('.calendar');
   707              if (cal.hasClass('left')) {
   708                  this.leftCalendar.month.add(1, 'month');
   709              } else {
   710                  this.rightCalendar.month.add(1, 'month');
   711              }
   712              this.updateCalendars();
   713          },
   714  
   715          hoverDate: function (e) {
   716              var title = $(e.target).attr('data-title');
   717              var row = title.substr(1, 1);
   718              var col = title.substr(3, 1);
   719              var cal = $(e.target).parents('.calendar');
   720  
   721              if (cal.hasClass('left')) {
   722                  this.container.find('input[name=daterangepicker_start]').val(this.leftCalendar.calendar[row][col].format(this.format));
   723              } else {
   724                  this.container.find('input[name=daterangepicker_end]').val(this.rightCalendar.calendar[row][col].format(this.format));
   725              }
   726          },
   727  
   728          setCustomDates: function(startDate, endDate) {
   729              this.chosenLabel = this.locale.customRangeLabel;
   730              if (startDate.isAfter(endDate)) {
   731                  var difference = this.endDate.diff(this.startDate);
   732                  endDate = moment(startDate).add(difference, 'ms');
   733              }
   734              this.startDate = startDate;
   735              this.endDate = endDate;
   736  
   737              this.updateView();
   738              this.updateCalendars();
   739          },
   740  
   741          clickDate: function (e) {
   742              var title = $(e.target).attr('data-title');
   743              var row = title.substr(1, 1);
   744              var col = title.substr(3, 1);
   745              var cal = $(e.target).parents('.calendar');
   746  
   747              var startDate, endDate;
   748              if (cal.hasClass('left')) {
   749                  startDate = this.leftCalendar.calendar[row][col];
   750                  endDate = this.endDate;
   751                  if (typeof this.dateLimit === 'object') {
   752                      var maxDate = moment(startDate).add(this.dateLimit).startOf('day');
   753                      if (endDate.isAfter(maxDate)) {
   754                          endDate = maxDate;
   755                      }
   756                  }
   757              } else {
   758                  startDate = this.startDate;
   759                  endDate = this.rightCalendar.calendar[row][col];
   760                  if (typeof this.dateLimit === 'object') {
   761                      var minDate = moment(endDate).subtract(this.dateLimit).startOf('day');
   762                      if (startDate.isBefore(minDate)) {
   763                          startDate = minDate;
   764                      }
   765                  }
   766              }
   767  
   768              if (this.singleDatePicker && cal.hasClass('left')) {
   769                  endDate = startDate.clone();
   770              } else if (this.singleDatePicker && cal.hasClass('right')) {
   771                  startDate = endDate.clone();
   772              }
   773  
   774              cal.find('td').removeClass('active');
   775  
   776              $(e.target).addClass('active');
   777  
   778              this.setCustomDates(startDate, endDate);
   779  
   780              if (!this.timePicker)
   781                  endDate.endOf('day');
   782  
   783              if (this.singleDatePicker && !this.timePicker)
   784                  this.clickApply();
   785          },
   786  
   787          clickApply: function (e) {
   788              this.updateInputText();
   789              this.hide();
   790              this.element.trigger('apply.daterangepicker', this);
   791          },
   792  
   793          clickCancel: function (e) {
   794              this.startDate = this.oldStartDate;
   795              this.endDate = this.oldEndDate;
   796              this.chosenLabel = this.oldChosenLabel;
   797              this.updateView();
   798              this.updateCalendars();
   799              this.hide();
   800              this.element.trigger('cancel.daterangepicker', this);
   801          },
   802  
   803          updateMonthYear: function (e) {
   804              var isLeft = $(e.target).closest('.calendar').hasClass('left'),
   805                  leftOrRight = isLeft ? 'left' : 'right',
   806                  cal = this.container.find('.calendar.'+leftOrRight);
   807  
   808              // Month must be Number for new moment versions
   809              var month = parseInt(cal.find('.monthselect').val(), 10);
   810              var year = cal.find('.yearselect').val();
   811  
   812              this[leftOrRight+'Calendar'].month.month(month).year(year);
   813              this.updateCalendars();
   814          },
   815  
   816          updateTime: function(e) {
   817  
   818              var cal = $(e.target).closest('.calendar'),
   819                  isLeft = cal.hasClass('left');
   820  
   821              var hour = parseInt(cal.find('.hourselect').val(), 10);
   822              var minute = parseInt(cal.find('.minuteselect').val(), 10);
   823  
   824              if (this.timePicker12Hour) {
   825                  var ampm = cal.find('.ampmselect').val();
   826                  if (ampm === 'PM' && hour < 12)
   827                      hour += 12;
   828                  if (ampm === 'AM' && hour === 12)
   829                      hour = 0;
   830              }
   831  
   832              if (isLeft) {
   833                  var start = this.startDate.clone();
   834                  start.hour(hour);
   835                  start.minute(minute);
   836                  this.startDate = start;
   837                  this.leftCalendar.month.hour(hour).minute(minute);
   838              } else {
   839                  var end = this.endDate.clone();
   840                  end.hour(hour);
   841                  end.minute(minute);
   842                  this.endDate = end;
   843                  this.rightCalendar.month.hour(hour).minute(minute);
   844              }
   845  
   846              this.updateCalendars();
   847          },
   848  
   849          updateCalendars: function () {
   850              this.leftCalendar.calendar = this.buildCalendar(this.leftCalendar.month.month(), this.leftCalendar.month.year(), this.leftCalendar.month.hour(), this.leftCalendar.month.minute(), 'left');
   851              this.rightCalendar.calendar = this.buildCalendar(this.rightCalendar.month.month(), this.rightCalendar.month.year(), this.rightCalendar.month.hour(), this.rightCalendar.month.minute(), 'right');
   852              this.container.find('.calendar.left').empty().html(this.renderCalendar(this.leftCalendar.calendar, this.startDate, this.minDate, this.maxDate));
   853              this.container.find('.calendar.right').empty().html(this.renderCalendar(this.rightCalendar.calendar, this.endDate, this.singleDatePicker ? this.minDate : this.startDate, this.maxDate));
   854              
   855              this.container.find('.ranges li').removeClass('active');
   856              var customRange = true;
   857              var i = 0;
   858              for (var range in this.ranges) {
   859                  if (this.timePicker) {
   860                      if (this.startDate.isSame(this.ranges[range][0]) && this.endDate.isSame(this.ranges[range][1])) {
   861                          customRange = false;
   862                          this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
   863                              .addClass('active').html();
   864                      }
   865                  } else {
   866                      //ignore times when comparing dates if time picker is not enabled
   867                      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')) {
   868                          customRange = false;
   869                          this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')')
   870                              .addClass('active').html();
   871                      }
   872                  }
   873                  i++;
   874              }
   875              if (customRange) {
   876                  this.chosenLabel = this.container.find('.ranges li:last').addClass('active').html();
   877                  this.showCalendars();
   878              }
   879          },
   880  
   881          buildCalendar: function (month, year, hour, minute, side) {
   882              var daysInMonth = moment([year, month]).daysInMonth();
   883              var firstDay = moment([year, month, 1]);
   884              var lastDay = moment([year, month, daysInMonth]);
   885              var lastMonth = moment(firstDay).subtract(1, 'month').month();
   886              var lastYear = moment(firstDay).subtract(1, 'month').year();
   887  
   888              var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
   889  
   890              var dayOfWeek = firstDay.day();
   891  
   892              var i;
   893  
   894              //initialize a 6 rows x 7 columns array for the calendar
   895              var calendar = [];
   896              calendar.firstDay = firstDay;
   897              calendar.lastDay = lastDay;
   898  
   899              for (i = 0; i < 6; i++) {
   900                  calendar[i] = [];
   901              }
   902  
   903              //populate the calendar with date objects
   904              var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
   905              if (startDay > daysInLastMonth)
   906                  startDay -= 7;
   907  
   908              if (dayOfWeek == this.locale.firstDay)
   909                  startDay = daysInLastMonth - 6;
   910  
   911              var curDate = moment([lastYear, lastMonth, startDay, 12, minute]);
   912              var col, row;
   913              for (i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
   914                  if (i > 0 && col % 7 === 0) {
   915                      col = 0;
   916                      row++;
   917                  }
   918                  calendar[row][col] = curDate.clone().hour(hour);
   919                  curDate.hour(12);
   920              }
   921  
   922              return calendar;
   923          },
   924  
   925          renderDropdowns: function (selected, minDate, maxDate) {
   926              var currentMonth = selected.month();
   927              var monthHtml = '<select class="monthselect">';
   928              var inMinYear = false;
   929              var inMaxYear = false;
   930  
   931              for (var m = 0; m < 12; m++) {
   932                  if ((!inMinYear || m >= minDate.month()) && (!inMaxYear || m <= maxDate.month())) {
   933                      monthHtml += "<option value='" + m + "'" +
   934                          (m === currentMonth ? " selected='selected'" : "") +
   935                          ">" + this.locale.monthNames[m] + "</option>";
   936                  }
   937              }
   938              monthHtml += "</select>";
   939  
   940              var currentYear = selected.year();
   941              var maxYear = (maxDate && maxDate.year()) || (currentYear + 5);
   942              var minYear = (minDate && minDate.year()) || (currentYear - 50);
   943              var yearHtml = '<select class="yearselect">';
   944  
   945              for (var y = minYear; y <= maxYear; y++) {
   946                  yearHtml += '<option value="' + y + '"' +
   947                      (y === currentYear ? ' selected="selected"' : '') +
   948                      '>' + y + '</option>';
   949              }
   950  
   951              yearHtml += '</select>';
   952  
   953              return monthHtml + yearHtml;
   954          },
   955  
   956          renderCalendar: function (calendar, selected, minDate, maxDate) {
   957  
   958              var html = '<div class="calendar-date">';
   959              html += '<table class="table-condensed">';
   960              html += '<thead>';
   961              html += '<tr>';
   962  
   963              // add empty cell for week number
   964              if (this.showWeekNumbers)
   965                  html += '<th></th>';
   966  
   967              if (!minDate || minDate.isBefore(calendar.firstDay)) {
   968                  html += '<th class="prev available"><i class="fa fa-angle-left"></i></th>';
   969              } else {
   970                  html += '<th></th>';
   971              }
   972  
   973              var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
   974  
   975              if (this.showDropdowns) {
   976                  dateHtml = this.renderDropdowns(calendar[1][1], minDate, maxDate);
   977              }
   978  
   979              html += '<th colspan="5" class="month">' + dateHtml + '</th>';
   980              if (!maxDate || maxDate.isAfter(calendar.lastDay)) {
   981                  html += '<th class="next available"><i class="fa fa-angle-right"></i></th>';
   982              } else {
   983                  html += '<th></th>';
   984              }
   985  
   986              html += '</tr>';
   987              html += '<tr>';
   988  
   989              // add week number label
   990              if (this.showWeekNumbers)
   991                  html += '<th class="week">' + this.locale.weekLabel + '</th>';
   992  
   993              $.each(this.locale.daysOfWeek, function (index, dayOfWeek) {
   994                  html += '<th>' + dayOfWeek + '</th>';
   995              });
   996  
   997              html += '</tr>';
   998              html += '</thead>';
   999              html += '<tbody>';
  1000  
  1001              for (var row = 0; row < 6; row++) {
  1002                  html += '<tr>';
  1003  
  1004                  // add week number
  1005                  if (this.showWeekNumbers)
  1006                      html += '<td class="week">' + calendar[row][0].week() + '</td>';
  1007  
  1008                  for (var col = 0; col < 7; col++) {
  1009                      var cname = 'available ';
  1010                      cname += (calendar[row][col].month() == calendar[1][1].month()) ? '' : 'off';
  1011  
  1012                      if ((minDate && calendar[row][col].isBefore(minDate, 'day')) || (maxDate && calendar[row][col].isAfter(maxDate, 'day'))) {
  1013                          cname = ' off disabled ';
  1014                      } else if (calendar[row][col].format('YYYY-MM-DD') == selected.format('YYYY-MM-DD')) {
  1015                          cname += ' active ';
  1016                          if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD')) {
  1017                              cname += ' start-date ';
  1018                          }
  1019                          if (calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD')) {
  1020                              cname += ' end-date ';
  1021                          }
  1022                      } else if (calendar[row][col] >= this.startDate && calendar[row][col] <= this.endDate) {
  1023                          cname += ' in-range ';
  1024                          if (calendar[row][col].isSame(this.startDate)) { cname += ' start-date '; }
  1025                          if (calendar[row][col].isSame(this.endDate)) { cname += ' end-date '; }
  1026                      }
  1027  
  1028                      var title = 'r' + row + 'c' + col;
  1029                      html += '<td class="' + cname.replace(/\s+/g, ' ').replace(/^\s?(.*?)\s?$/, '$1') + '" data-title="' + title + '">' + calendar[row][col].date() + '</td>';
  1030                  }
  1031                  html += '</tr>';
  1032              }
  1033  
  1034              html += '</tbody>';
  1035              html += '</table>';
  1036              html += '</div>';
  1037  
  1038              var i;
  1039              if (this.timePicker) {
  1040  
  1041                  html += '<div class="calendar-time">';
  1042                  html += '<select class="hourselect">';
  1043                  var start = 0;
  1044                  var end = 23;
  1045                  var selected_hour = selected.hour();
  1046                  if (this.timePicker12Hour) {
  1047                      start = 1;
  1048                      end = 12;
  1049                      if (selected_hour >= 12)
  1050                          selected_hour -= 12;
  1051                      if (selected_hour === 0)
  1052                          selected_hour = 12;
  1053                  }
  1054  
  1055                  for (i = start; i <= end; i++) {
  1056                      if (i == selected_hour) {
  1057                          html += '<option value="' + i + '" selected="selected">' + i + '</option>';
  1058                      } else {
  1059                          html += '<option value="' + i + '">' + i + '</option>';
  1060                      }
  1061                  }
  1062  
  1063                  html += '</select> : ';
  1064  
  1065                  html += '<select class="minuteselect">';
  1066  
  1067                  for (i = 0; i < 60; i += this.timePickerIncrement) {
  1068                      var num = i;
  1069                      if (num < 10)
  1070                          num = '0' + num;
  1071                      if (i == selected.minute()) {
  1072                          html += '<option value="' + i + '" selected="selected">' + num + '</option>';
  1073                      } else {
  1074                          html += '<option value="' + i + '">' + num + '</option>';
  1075                      }
  1076                  }
  1077  
  1078                  html += '</select> ';
  1079  
  1080                  if (this.timePicker12Hour) {
  1081                      html += '<select class="ampmselect">';
  1082                      if (selected.hour() >= 12) {
  1083                          html += '<option value="AM">AM</option><option value="PM" selected="selected">PM</option>';
  1084                      } else {
  1085                          html += '<option value="AM" selected="selected">AM</option><option value="PM">PM</option>';
  1086                      }
  1087                      html += '</select>';
  1088                  }
  1089  
  1090                  html += '</div>';
  1091  
  1092              }
  1093  
  1094              return html;
  1095  
  1096          },
  1097  
  1098          remove: function() {
  1099  
  1100              this.container.remove();
  1101              this.element.off('.daterangepicker');
  1102              this.element.removeData('daterangepicker');
  1103  
  1104          }
  1105  
  1106      };
  1107  
  1108      $.fn.daterangepicker = function (options, cb) {
  1109          this.each(function () {
  1110              var el = $(this);
  1111              if (el.data('daterangepicker'))
  1112                  el.data('daterangepicker').remove();
  1113              el.data('daterangepicker', new DateRangePicker(el, options, cb));
  1114          });
  1115          return this;
  1116      };
  1117  
  1118  }));