|
/** |
|
* @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> ' + |
|
'<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 |