date picker
[scannr.git] / js / daterangepicker.js
blob:a/js/daterangepicker.js -> blob:b/js/daterangepicker.js
  /**
  * @version: 1.0.1
  * @author: Dan Grossman http://www.dangrossman.info/
  * @date: 2012-08-20
  * @copyright: Copyright (c) 2012 Dan Grossman. All rights reserved.
  * @license: Licensed under Apache License v2.0. See http://www.apache.org/licenses/LICENSE-2.0
  * @website: http://www.improvely.com/
  */
  !function ($) {
   
  var DateRangePicker = function (element, options, cb) {
  var hasOptions = typeof options == 'object'
  var localeObject;
   
  //state
  this.startDate = Date.create('today');
  this.endDate = Date.create('today');
  this.minDate = false;
  this.maxDate = false;
  this.changed = false;
  this.cleared = false;
  this.ranges = {};
  this.opens = 'right';
  this.cb = function () { };
  this.format = '{MM}/{dd}/{yyyy}';
  this.separator = ' - ';
  this.showWeekNumbers = false;
  this.buttonClasses = ['btn-success'];
  this.locale = {
  applyLabel: 'Apply',
  clearLabel:"Clear",
  fromLabel: 'From',
  toLabel: 'To',
  weekLabel: 'W',
  customRangeLabel: 'Custom Range',
  daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
  monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  firstDay: 0
  };
   
  localeObject = this.locale;
   
  this.leftCalendar = {
  month: Date.create('today').set({ day: 1, month: this.startDate.getMonth(), year: this.startDate.getFullYear() }),
  calendar: Array()
  };
   
  this.rightCalendar = {
  month: Date.create('today').set({ day: 1, month: this.endDate.getMonth(), year: this.endDate.getFullYear() }),
  calendar: Array()
  };
   
  //element that triggered the date range picker
  this.element = $(element);
   
  if (this.element.hasClass('pull-right'))
  this.opens = 'left';
   
  if (this.element.is('input')) {
  this.element.on({
  click: $.proxy(this.show, this),
  focus: $.proxy(this.show, this)
  });
  } else {
  this.element.on('click', $.proxy(this.show, this));
  }
   
  if (hasOptions) {
  if(typeof options.locale == 'object') {
  $.each(localeObject, function (property, value) {
  localeObject[property] = options.locale[property] || value;
  });
  }
  }
   
  var DRPTemplate = '<div class="daterangepicker dropdown-menu">' +
  '<div class="calendar left"></div>' +
  '<div class="calendar right"></div>' +
  '<div class="ranges">' +
  '<div class="range_inputs">' +
  '<div class="daterangepicker_start_input" style="float: left">' +
  '<label for="daterangepicker_start">' + this.locale.fromLabel + '</label>' +
  '<input class="input-mini" type="text" name="daterangepicker_start" value="" disabled="disabled" />' +
  '</div>' +
  '<div class="daterangepicker_end_input" style="float: left; padding-left: 11px">' +
  '<label for="daterangepicker_end">' + this.locale.toLabel + '</label>' +
  '<input class="input-mini" type="text" name="daterangepicker_end" value="" disabled="disabled" />' +
  '</div>' +
  '<button class="btn btn-small btn-success applyBtn" disabled="disabled">' + this.locale.applyLabel + '</button>&nbsp;' +
  '<button class="btn btn-small clearBtn">' + this.locale.clearLabel + '</button>' +
  '</div>' +
  '</div>' +
  '</div>';
   
  this.container = $(DRPTemplate).appendTo('body');
   
  if (hasOptions) {
   
  if (typeof options.format == 'string')
  this.format = options.format;
   
  if (typeof options.separator == 'string')
  this.separator = options.separator;
   
  if (typeof options.startDate == 'string')
  this.startDate = Date.create(options.startDate);
   
  if (typeof options.endDate == 'string')
  this.endDate = Date.create(options.endDate);
   
  if (typeof options.minDate == 'string')
  this.minDate = Date.create(options.minDate);
   
  if (typeof options.maxDate == 'string')
  this.maxDate = Date.create(options.maxDate);
   
   
  if (typeof options.startDate == 'object')
  this.startDate = options.startDate;
   
  if (typeof options.endDate == 'object')
  this.endDate = options.endDate;
   
  if (typeof options.minDate == 'object')
  this.minDate = options.minDate;
   
  if (typeof options.maxDate == 'object')
  this.maxDate = options.maxDate;
   
  if (typeof options.ranges == 'object') {
  for (var range in options.ranges) {
   
  var start = options.ranges[range][0];
  var end = options.ranges[range][1];
   
  if (typeof start == 'string')
  start = Date.create(start);
   
  if (typeof end == 'string')
  end = Date.create(end);
   
  // If we have a min/max date set, bound this range
  // to it, but only if it would otherwise fall
  // outside of the min/max.
  if (this.minDate && start < this.minDate)
  start = this.minDate;
   
  if (this.maxDate && end > this.maxDate)
  end = this.maxDate;
   
  // If the end of the range is before the minimum (if min is set) OR
  // the start of the range is after the max (also if set) don't display this
  // range option.
  if ((this.minDate && end < this.minDate) || (this.maxDate && start > this.maxDate))
  {
  continue;
  }
   
  this.ranges[range] = [start, end];
  }
   
  var list = '<ul>';
  for (var range in this.ranges) {
  list += '<li>' + range + '</li>';
  }
  list += '<li>' + this.locale.customRangeLabel + '</li>';
  list += '</ul>';
  this.container.find('.ranges').prepend(list);
  }
   
  // update day names order to firstDay
  if (typeof options.locale == 'object') {
  if (typeof options.locale.firstDay == 'number') {
  this.locale.firstDay = options.locale.firstDay;
  var iterator = options.locale.firstDay;
  while (iterator > 0) {
  this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
  iterator--;
  }
  }
  }
   
  if (typeof options.opens == 'string')
  this.opens = options.opens;
   
  if (typeof options.showWeekNumbers == 'boolean') {
  this.showWeekNumbers = options.showWeekNumbers;
  }
   
  if (typeof options.buttonClasses == 'string') {
  this.buttonClasses = [options.buttonClasses];
  }
   
  if (typeof options.buttonClasses == 'object') {
  this.buttonClasses = options.buttonClasses;
  }
   
  }
   
  //apply CSS classes to buttons
  var c = this.container;
  $.each(this.buttonClasses, function (idx, val) {
  c.find('button').addClass(val);
  });
   
  if (this.opens == 'right') {
  //swap calendar positions
  var left = this.container.find('.calendar.left');
  var right = this.container.find('.calendar.right');
  left.removeClass('left').addClass('right');
  right.removeClass('right').addClass('left');
  }
   
  if (typeof options == 'undefined' || typeof options.ranges == 'undefined')
  this.container.find('.calendar').show();
   
  if (typeof cb == 'function')
  this.cb = cb;
   
  this.container.addClass('opens' + this.opens);
   
  //event listeners
  this.container.on('mousedown', $.proxy(this.mousedown, this));
  this.container.find('.calendar').on('click', '.prev', $.proxy(this.clickPrev, this));
  this.container.find('.calendar').on('click', '.next', $.proxy(this.clickNext, this));
  this.container.find('.ranges').on('click', 'button.applyBtn', $.proxy(this.clickApply, this));
  this.container.find('.ranges').on('click', 'button.clearBtn', $.proxy(this.clickClear, this));
   
  this.container.find('.calendar').on('click', 'td.available', $.proxy(this.clickDate, this));
  this.container.find('.calendar').on('mouseenter', 'td.available', $.proxy(this.enterDate, this));
  this.container.find('.calendar').on('mouseleave', 'td.available', $.proxy(this.updateView, this));
   
  this.container.find('.ranges').on('click', 'li', $.proxy(this.clickRange, this));
  this.container.find('.ranges').on('mouseenter', 'li', $.proxy(this.enterRange, this));
  this.container.find('.ranges').on('mouseleave', 'li', $.proxy(this.updateView, this));
   
  this.element.on('keyup', $.proxy(this.updateFromControl, this));
   
  this.updateView();
  this.updateCalendars();
   
  };
   
  DateRangePicker.prototype = {
   
  constructor: DateRangePicker,
   
  mousedown: function (e) {
  e.stopPropagation();
  e.preventDefault();
  },
   
  updateView: function () {
  this.leftCalendar.month.set({ month: this.startDate.getMonth(), year: this.startDate.getFullYear() });
  this.rightCalendar.month.set({ month: this.endDate.getMonth(), year: this.endDate.getFullYear() });
   
  this.container.find('input[name=daterangepicker_start]').val(this.startDate.format(this.format));
  this.container.find('input[name=daterangepicker_end]').val(this.endDate.format(this.format));
   
  if (this.startDate.is(this.endDate) || this.startDate.isBefore(this.endDate)) {
  this.container.find('button.applyBtn').removeAttr('disabled');
  } else {
  this.container.find('button.applyBtn').attr('disabled', 'disabled');
  }
  },
   
  updateFromControl: function () {
  if (!this.element.is('input')) return;
   
  var dateString = this.element.val().split(this.separator);
  var start = Date.create(dateString[0]);
  var end = Date.create(dateString[1]);
   
  if (start == null || end == null) return;
  if (end.isBefore(start)) return;
   
  this.startDate = start;
  this.endDate = end;
   
  this.updateView();
  this.cb(this.startDate, this.endDate);
  this.updateCalendars();
  },
   
  notify: function () {
  if (!this.cleared) {
  this.updateView();
  }
   
  if (this.element.is('input')) {
  this.element.val(this.cleared ? '' : this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
  }
  var arg1 = (this.cleared ? null : this.startDate),
  arg2 = (this.cleared ? null : this.endDate);
  this.cleared = false;
  this.cb(arg1,arg2);
  },
   
  move: function () {
  if (this.opens == 'left') {
  this.container.css({
  top: this.element.offset().top + this.element.outerHeight(),
  right: $(window).width() - this.element.offset().left - this.element.outerWidth(),
  left: 'auto'
  });
  } else {
  this.container.css({
  top: this.element.offset().top + this.element.outerHeight(),
  left: this.element.offset().left,
  right: 'auto'
  });
  }
  },
   
  show: function (e) {
  this.container.show();
  this.move();
   
  if (e) {
  e.stopPropagation();
  e.preventDefault();
  }
   
  this.changed = false;
   
  this.element.trigger('shown',{target:e.target,picker:this});
   
  $(document).on('mousedown', $.proxy(this.hide, this));
  },
   
  hide: function (e) {
  this.container.hide();
  $(document).off('mousedown', this.hide);
   
  if (this.changed) {
  this.changed = false;
  this.notify();
  }
  },
   
  enterRange: function (e) {
  var label = e.target.innerHTML;
  if (label == this.locale.customRangeLabel) {
  this.updateView();
  } else {
  var dates = this.ranges[label];
  this.container.find('input[name=daterangepicker_start]').val(dates[0].format(this.format));
  this.container.find('input[name=daterangepicker_end]').val(dates[1].format(this.format));
  }
  },
   
  clickRange: function (e) {
  var label = e.target.innerHTML;
  if (label == this.locale.customRangeLabel) {
  this.container.find('.calendar').show();
  } else {
  var dates = this.ranges