You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
819 lines
25 KiB
JavaScript
819 lines
25 KiB
JavaScript
5 years ago
|
/*!
|
||
|
* glDatePicker v2.0
|
||
|
* http://glad.github.com/glDatePicker/
|
||
|
*
|
||
|
* Copyright (c) 2013 Gautam Lad. All rights reserved.
|
||
|
* Released under the MIT license.
|
||
|
*
|
||
|
* Date: Tue Jan 1 2013
|
||
|
*/
|
||
|
;(function() {
|
||
|
$.fn.glDatePicker = function(options) {
|
||
|
var pluginName = 'glDatePicker';
|
||
|
|
||
|
// Find the plugin attached to the element
|
||
|
var instance = this.data(pluginName);
|
||
|
|
||
|
// If the instance wasn't found, create it...
|
||
|
if(!instance) {
|
||
|
// Return the element being bound to
|
||
|
return this.each(function() {
|
||
|
return $(this).data(pluginName, new glDatePicker(this, options));
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// ...otherwise if the user passes true to the plugin (on the second call),
|
||
|
// then return the instance of the plugin itself
|
||
|
return (options === true) ? instance : this;
|
||
|
};
|
||
|
|
||
|
// Default options
|
||
|
$.fn.glDatePicker.defaults =
|
||
|
{
|
||
|
// Style to use for the calendar. This name must match the name used in
|
||
|
// the stylesheet, using the class naming convention "gldp-cssName".
|
||
|
cssName: 'default',
|
||
|
|
||
|
// The z-index for the calendar control.
|
||
|
zIndex: 1000,
|
||
|
|
||
|
// Thickness of border (in pixels)
|
||
|
borderSize: 1,
|
||
|
|
||
|
// The number of pixels to offset the calendar's position on the page.
|
||
|
calendarOffset: { x: 0, y: 1 },
|
||
|
|
||
|
// Set to true if you want the calendar to be visible at all times.
|
||
|
// NOTE: If your target element is hidden, the calendar will be hidden as well.
|
||
|
showAlways: false,
|
||
|
|
||
|
// Hide the calendar when a date is selected (only if showAlways is set to false).
|
||
|
hideOnClick: true,
|
||
|
|
||
|
// Allow selection of months by clicking on the month in the title.
|
||
|
allowMonthSelect: true,
|
||
|
|
||
|
// Allow selection of years by clicking on the year in the title.
|
||
|
allowYearSelect: true,
|
||
|
|
||
|
// The date that will be treated as 'today'.
|
||
|
todayDate: new Date(),
|
||
|
|
||
|
// The date that will appear selected when the calendar renders.
|
||
|
// By default it will be set to todayDate.
|
||
|
selectedDate: null,
|
||
|
|
||
|
// Arrows used for the Previous and Next month buttons on the title.
|
||
|
// Set these to blank to hide the arrows completely.
|
||
|
prevArrow: '\u25c4',
|
||
|
nextArrow: '\u25ba',
|
||
|
|
||
|
// A collection of dates that can be selectable by the user.
|
||
|
// The dates can be a one-time selection or made repeatable by setting
|
||
|
// the repeatYear or repeatMonth flag to true.
|
||
|
// By default repeatYear and repeatMonth are false.
|
||
|
//
|
||
|
// This example creates 4-individual dates that can be selected;
|
||
|
// The first date will repeat every year, the second date will repeat every
|
||
|
// month and year, the third date will repeat every month and the fourth date
|
||
|
// will only be selectable one-time and not repeat:
|
||
|
//
|
||
|
// selectableDates: [
|
||
|
// { date: new Date(0, 8, 5), repeatYear: true },
|
||
|
// { date: new Date(0, 0, 14), repeatMonth: true, repeatYear: true },
|
||
|
// { date: new Date(2013, 0, 24), repeatMonth: true },
|
||
|
// { date: new Date(2013, 11, 25) },
|
||
|
// ]
|
||
|
selectableDates: null,
|
||
|
|
||
|
// A collection of date ranges that are selectable by the user.
|
||
|
// The ranges can be made to repeat by setting repeatYear to true
|
||
|
// (repeatMonth is not supported).
|
||
|
//
|
||
|
// This example will create 3-sets of selectable date ranges with
|
||
|
// specific from and to ranges. The 4th and 5th ranges don't specify
|
||
|
// the "to" date in which case the "to" date will be the maximum days for
|
||
|
// the month specified in "from". The 4th and 5th ranges also repeat every year:
|
||
|
//
|
||
|
// selectableDateRange: [
|
||
|
// { from: new Date(2013, 1, 1), to: newDate (2013, 2, 1) },
|
||
|
// { from: new Date(2013, 4, 1), to: newDate (2013, 8, 1) },
|
||
|
// { from: new Date(2013, 7, 10), to: newDate (2013, 9, 10) },
|
||
|
// { from: new Date(0, 8, 10), repeatYear: true }
|
||
|
// { from: new Date(0, 9, 1), repeatYear: true }
|
||
|
// ]
|
||
|
selectableDateRange: null,
|
||
|
|
||
|
// Mark certain dates as special dates. Similar to selectableDates, this
|
||
|
// property supports both repeatYear and repeatMonth flags.
|
||
|
// Each special date can be styled using custom style names and can have
|
||
|
// data attached to it that will be returned in the onClick callback.
|
||
|
// The data field can be any custom (JSON style) object.
|
||
|
//
|
||
|
// This example creates two (repeatable by year) dates with special data in them.
|
||
|
// The first date also assigns a special class (which you will have to define).
|
||
|
// specialDates: [
|
||
|
// {
|
||
|
// date: new Date(0, 8, 5),
|
||
|
// data: { message: 'Happy Birthday!' },
|
||
|
// repeatYear: true,
|
||
|
// cssClass: 'special-bday'
|
||
|
// },
|
||
|
// {
|
||
|
// date: new Date(2013, 0, 8),
|
||
|
// data: { message: 'Meeting every day 8 of the month' },
|
||
|
// repeatMonth: true
|
||
|
// }
|
||
|
// ]
|
||
|
specialDates: null,
|
||
|
|
||
|
// List of months that can be selectable, including when the user clicks
|
||
|
// on the title to select from the dropdown.
|
||
|
// This example only makes two months visible; September and December:
|
||
|
// selectableMonths: [8, 11]
|
||
|
selectableMonths : null,
|
||
|
|
||
|
// List of selectable years. If not provided, will default to 5-years
|
||
|
// back and forward.
|
||
|
// This example only allows selection of dates that have year 2012, 2013, 2015
|
||
|
// selectableYears: [2012, 2013, 2015]
|
||
|
selectableYears: null,
|
||
|
|
||
|
// List of selectable days of the week. 0 is Sunday, 1 is Monday, and so on.
|
||
|
// This example allows only Sunday, Tuesday, Thursday:
|
||
|
// selectableDOW: [0, 2, 4]
|
||
|
selectableDOW : null,
|
||
|
|
||
|
// Names of the month that will be shown in the title.
|
||
|
// Will default to long-form names:
|
||
|
// January, February, March, April, May, June, July,
|
||
|
// August, September, October, November, December
|
||
|
monthNames: null,
|
||
|
|
||
|
// Names of the days of the Week that will be shown below the title.
|
||
|
// Will default to short-form names:
|
||
|
// Sun, Mon, Tue, Wed, Thu, Fri, Sat
|
||
|
dowNames: null,
|
||
|
|
||
|
// The day of the week to start the calendar on. 0 is Sunday, 1 is Monday and so on.
|
||
|
dowOffset: 0,
|
||
|
|
||
|
// Callback that will trigger when the user clicks a selectable date.
|
||
|
// Parameters that are passed to the callback:
|
||
|
// el : The input element the date picker is bound to
|
||
|
// cell : The cell on the calendar that triggered this event
|
||
|
// date : The date associated with the cell
|
||
|
// data : Special data associated with the cell (if available, otherwise, null)
|
||
|
onClick: (function(el, cell, date, data) {
|
||
|
el.val(date.toLocaleDateString());
|
||
|
}),
|
||
|
|
||
|
// Callback that will trigger when the user hovers over a selectable date.
|
||
|
// This callback receives the same set of parameters as onClick.
|
||
|
onHover: function(el, cell, date, data) {},
|
||
|
|
||
|
// Callback that will trigger when the calendar needs to show.
|
||
|
// You can use this callback to animate the opening of the calendar.
|
||
|
onShow: function(calendar) { calendar.show(); },
|
||
|
|
||
|
// Callback that will trigger when the calendar needs to hide.
|
||
|
// You can use this callback to animate the hiding of the calendar.
|
||
|
onHide: function(calendar) { calendar.hide(); },
|
||
|
|
||
|
// First date of the month.
|
||
|
firstDate: null
|
||
|
};
|
||
|
|
||
|
// Our plugin object
|
||
|
var glDatePicker = (function() {
|
||
|
// Main entry point. Initialize the plugin
|
||
|
function glDatePicker(element, userOptions) {
|
||
|
// Grab handle to this
|
||
|
var self = this;
|
||
|
|
||
|
// Save bound element to el
|
||
|
self.el = $(element);
|
||
|
var el = self.el;
|
||
|
|
||
|
// Merge user options into default options
|
||
|
self.options = $.extend(true, {}, $.fn.glDatePicker.defaults, userOptions);
|
||
|
var options = self.options;
|
||
|
|
||
|
// Find the calendar element if the user provided one
|
||
|
self.calendar = $($.find('[gldp-el=' + el.attr('gldp-id') + ' ]'));
|
||
|
|
||
|
// Default first date to selected
|
||
|
options.selectedDate = options.selectedDate || options.todayDate;
|
||
|
options.firstDate = (new Date((options.firstDate || options.selectedDate)))._first();
|
||
|
|
||
|
if(!(el.attr('gldp-id') || '').length) {
|
||
|
el.attr('gldp-id', 'gldp-' + Math.round(Math.random() * 1e10))
|
||
|
}
|
||
|
|
||
|
// Show the plugin on focus
|
||
|
el
|
||
|
.addClass('gldp-el')
|
||
|
.bind('click', function(e) { self.show(e); })
|
||
|
.bind('focus', function(e) { self.show(e); });
|
||
|
|
||
|
// If the user is defining the container and it exists, hide it on initial creation.
|
||
|
// The update function will handle showing if it's showAlways = true
|
||
|
if(self.calendar.length && !options.showAlways) {
|
||
|
self.calendar.hide();
|
||
|
}
|
||
|
|
||
|
// Hide the plugin on mouse up outside of the plugin
|
||
|
$(document).bind('mouseup', function(e) {
|
||
|
var target = e.target;
|
||
|
var calendar = self.calendar;
|
||
|
|
||
|
if(!el.is(target) && !calendar.is(target) && calendar.has(target).length === 0 && calendar.is(':visible')) {
|
||
|
self.hide();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Render calendar
|
||
|
self.render();
|
||
|
};
|
||
|
|
||
|
// Public methods
|
||
|
glDatePicker.prototype =
|
||
|
{
|
||
|
show: function() {
|
||
|
// Hide others and show this calendar
|
||
|
$.each($('.gldp-el').not(this.el), function(i, o) {
|
||
|
if(o.length) { o.options.onHide(o.calendar) ; }
|
||
|
});
|
||
|
|
||
|
// Show this calendar
|
||
|
this.options.onShow(this.calendar);
|
||
|
},
|
||
|
|
||
|
hide: function() {
|
||
|
if(this.options && !this.options.showAlways) {
|
||
|
this.options.onHide(this.calendar);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Render the calendar
|
||
|
render: function(renderCalback) {
|
||
|
var self = this;
|
||
|
var el = self.el;
|
||
|
var options = self.options;
|
||
|
var calendar = self.calendar;
|
||
|
|
||
|
// Build a core class (with border) that every element would have
|
||
|
var coreClass = ' core border ';
|
||
|
var cssName = 'gldp-' + options.cssName;
|
||
|
|
||
|
// Get today
|
||
|
var todayVal = options.todayDate._val();
|
||
|
var todayTime = todayVal.time;
|
||
|
|
||
|
// Constants
|
||
|
var maxRow = 6;
|
||
|
var maxCol = 7;
|
||
|
var borderSize = options.borderSize + 'px';
|
||
|
|
||
|
// Helper function to build selectable list
|
||
|
var getSelectableList = function(min, max, userList) {
|
||
|
// Build a default list using min/max
|
||
|
var resultList = [];
|
||
|
for(var i = min; i <= max; i++) { resultList.push(i); }
|
||
|
|
||
|
// If user provided a collection, sanitize list by ensuring it's within range and unique
|
||
|
if(userList) {
|
||
|
var newList = [];
|
||
|
$.each(userList, function(i, v) {
|
||
|
if(v >= min && v <= max && newList._indexOf(v) < 0) {
|
||
|
newList.push(v);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
resultList = newList.length ? newList : resultList;
|
||
|
};
|
||
|
|
||
|
// Sort the values before returning it
|
||
|
resultList.sort();
|
||
|
|
||
|
return resultList;
|
||
|
};
|
||
|
|
||
|
// Selectable (constants)
|
||
|
var selectableMonths = getSelectableList(0, 11, options.selectableMonths);
|
||
|
var selectableYears = getSelectableList(todayVal.year - 5, todayVal.year + 5, options.selectableYears);
|
||
|
var selectableDOW = getSelectableList(0, 6, options.selectableDOW);
|
||
|
var dowNames = options.dowNames || [ '日', '一', '二', '三', '四', '五', '六' ];
|
||
|
var monthNames = options.monthNames || [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ];
|
||
|
|
||
|
// Create cell width based on el size
|
||
|
var containerWidth = el.outerWidth();
|
||
|
var containerHeight = containerWidth;
|
||
|
|
||
|
// Create cell size based on container size
|
||
|
var getCellSize = function(_size, _count) {
|
||
|
return (_size / _count) + ((options.borderSize / _count) * (_count - 1));
|
||
|
};
|
||
|
var cellWidth = getCellSize(containerWidth, maxCol);
|
||
|
var cellHeight = getCellSize(containerHeight, maxRow + 2);
|
||
|
|
||
|
// If calendar doesn't exist, create it and re-assign it to self
|
||
|
if(!calendar.length) {
|
||
|
self.calendar = calendar = $('<div/>')
|
||
|
.attr('gldp-el', el.attr('gldp-id'))
|
||
|
.data('is', true)
|
||
|
.css(
|
||
|
{
|
||
|
display: (options.showAlways ? undefined : 'none'),
|
||
|
zIndex: options.zIndex,
|
||
|
width: (cellWidth * maxCol) + 'px'
|
||
|
});
|
||
|
|
||
|
$('body').append(calendar);
|
||
|
}
|
||
|
else {
|
||
|
if(!eval(calendar.data('is'))) {
|
||
|
containerWidth = calendar.outerWidth();
|
||
|
containerHeight = calendar.outerHeight();
|
||
|
|
||
|
cellWidth = getCellSize(containerWidth, maxCol);
|
||
|
cellHeight = getCellSize(containerHeight, maxRow + 2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Hide calendar if the target element isn't visible
|
||
|
if(!el.is(':visible')) { calendar.hide(); }
|
||
|
|
||
|
// Add core classes and remove calendar's children
|
||
|
calendar
|
||
|
.removeClass()
|
||
|
.addClass(cssName)
|
||
|
.children().remove();
|
||
|
|
||
|
// Bind to resize event to position calendar
|
||
|
var onResize = function() {
|
||
|
var elPos = el.offset();
|
||
|
calendar.css(
|
||
|
{
|
||
|
top: (elPos.top + el.outerHeight() + options.calendarOffset.y) + 'px',
|
||
|
left: (elPos.left + options.calendarOffset.x) + 'px'
|
||
|
});
|
||
|
};
|
||
|
$(window).resize(onResize);
|
||
|
onResize();
|
||
|
|
||
|
// Create variables for cells
|
||
|
var cellCSS =
|
||
|
{
|
||
|
width: cellWidth + 'px',
|
||
|
height: cellHeight + 'px',
|
||
|
lineHeight: cellHeight + 'px'
|
||
|
};
|
||
|
|
||
|
// Helper function to setDate
|
||
|
var setFirstDate = function(_date) {
|
||
|
if(_date) {
|
||
|
// Get first date
|
||
|
options.firstDate = _date;
|
||
|
|
||
|
// Update the calendar
|
||
|
self.render();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var getFirstDate = function(_offset) {
|
||
|
// Create start date as the first date of the month
|
||
|
var _date = new Date(options.firstDate);
|
||
|
|
||
|
// Default to no offset
|
||
|
_offset = _offset || 0;
|
||
|
|
||
|
// Find out which months are selectable
|
||
|
while(true) {
|
||
|
// Adjust date for month offset
|
||
|
_date.setMonth(_date.getMonth() + _offset);
|
||
|
_date.setDate(Math.min(1, _date._max()));
|
||
|
|
||
|
// If not an offset, break out of the loop
|
||
|
if(_offset == 0) { break; }
|
||
|
|
||
|
// Get _date's value
|
||
|
var dateVal = _date._val();
|
||
|
|
||
|
// Get local vars
|
||
|
var dateMonth = dateVal.month;
|
||
|
var dateYear = dateVal.year;
|
||
|
|
||
|
// Find the month first
|
||
|
if(selectableMonths._indexOf(dateMonth) != -1) {
|
||
|
// If year is in our collection, break...
|
||
|
if(selectableYears._indexOf(dateYear) != -1) {
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
// ...otherwise, if it's out of bounds, exit loop
|
||
|
if(dateYear < selectableYears[0] || dateYear > selectableYears[selectableYears.length - 1]) {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return _date;
|
||
|
};
|
||
|
|
||
|
// Get the previous, next first dates
|
||
|
var prevFirstDate = getFirstDate(-1);
|
||
|
var nextFirstDate = getFirstDate(1);
|
||
|
|
||
|
// Get the first date for the current month being rendered
|
||
|
var firstDate = (options.firstDate = getFirstDate());
|
||
|
var firstDateVal = firstDate._val();
|
||
|
var firstDateMonth = firstDateVal.month;
|
||
|
var firstDateYear = firstDateVal.year;
|
||
|
|
||
|
// Get the start date in the calendar
|
||
|
var startDate = new Date(firstDate);
|
||
|
|
||
|
// Sanitize days of the week offset
|
||
|
var dowOffset = Math.abs(Math.min(6, Math.max(0, options.dowOffset)));
|
||
|
|
||
|
// Offset weekdays
|
||
|
var startOffset = startDate.getDay() - dowOffset;
|
||
|
startOffset = startOffset < 1 ? -7 - startOffset : -startOffset;
|
||
|
dowNames = (dowNames.concat(dowNames))
|
||
|
.slice(dowOffset, dowOffset + 7);
|
||
|
|
||
|
// Offset the start date
|
||
|
startDate._add(startOffset);
|
||
|
|
||
|
// Gather flags for prev/next arrows
|
||
|
var showPrev = (prevFirstDate);
|
||
|
var showNext = (nextFirstDate);
|
||
|
|
||
|
// Create the arrows and title
|
||
|
var monyearClass = coreClass + 'monyear ';
|
||
|
|
||
|
var prevCell = $('<div/>')
|
||
|
.addClass(monyearClass)
|
||
|
.css(
|
||
|
$.extend({}, cellCSS,
|
||
|
{
|
||
|
borderWidth: borderSize + ' 0 0 ' + borderSize
|
||
|
})
|
||
|
)
|
||
|
.append(
|
||
|
$('<a/>')
|
||
|
.addClass('prev-arrow' + (showPrev ? '' : '-off'))
|
||
|
.html(options.prevArrow)
|
||
|
)
|
||
|
.mousedown(function() { return false; })
|
||
|
.click(function(e) {
|
||
|
if(options.prevArrow != '' && showPrev) {
|
||
|
e.stopPropagation();
|
||
|
setFirstDate(prevFirstDate);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var titleCellCount = maxCol - 2;
|
||
|
var titleWidth = (cellWidth * titleCellCount) - (titleCellCount * options.borderSize) + (options.borderSize);
|
||
|
var titleCell = $('<div/>')
|
||
|
.addClass(monyearClass + 'title')
|
||
|
.css(
|
||
|
$.extend({}, cellCSS,
|
||
|
{
|
||
|
width: titleWidth + 'px',
|
||
|
borderTopWidth: borderSize,
|
||
|
marginLeft: '-' + (borderSize)
|
||
|
})
|
||
|
);
|
||
|
|
||
|
var nextCell = $('<div/>')
|
||
|
.addClass(monyearClass)
|
||
|
.css(
|
||
|
$.extend({}, cellCSS,
|
||
|
{
|
||
|
marginLeft: '-' + (borderSize),
|
||
|
borderWidth: borderSize + ' ' + borderSize + ' 0 0'
|
||
|
})
|
||
|
)
|
||
|
.append(
|
||
|
$('<a/>')
|
||
|
.addClass('next-arrow' + (showNext ? '' : '-off'))
|
||
|
.html(options.nextArrow)
|
||
|
)
|
||
|
.mousedown(function() { return false; })
|
||
|
.click(function(e) {
|
||
|
if(options.nextArrow != '' && showNext) {
|
||
|
e.stopPropagation();
|
||
|
setFirstDate(nextFirstDate);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Add cells for prev/title/next
|
||
|
calendar
|
||
|
.append(prevCell)
|
||
|
.append(titleCell)
|
||
|
.append(nextCell);
|
||
|
|
||
|
// Add all the cells to the calendar
|
||
|
for(var row = 0, cellIndex = 0; row < maxRow + 1; row++) {
|
||
|
for(var col = 0; col < maxCol; col++, cellIndex++) {
|
||
|
var cellDate = new Date(startDate);
|
||
|
var cellClass = 'day';
|
||
|
var cellZIndex = options.zIndex + (cellIndex);
|
||
|
var cell = $('<div/>')
|
||
|
|
||
|
if(!row) {
|
||
|
cellClass = 'dow';
|
||
|
cell.html(dowNames[col]);
|
||
|
cellDate = null;
|
||
|
}
|
||
|
else {
|
||
|
// Get the new date for this cell
|
||
|
cellDate._add(col + ((row - 1) * maxCol));
|
||
|
|
||
|
// Get value for this date
|
||
|
var cellDateVal = cellDate._val();
|
||
|
var cellDateTime = cellDateVal.time;
|
||
|
|
||
|
// Variable to hold special data
|
||
|
var specialData = null;
|
||
|
|
||
|
// Determine if this date is selectable
|
||
|
var isSelectable = true;
|
||
|
|
||
|
// Helper function to get repeat friendly date against current date
|
||
|
var getRepeatDate = function(v, date) {
|
||
|
// If repeating, set the date's year and month accordingly
|
||
|
if(v.repeatYear === true) { date.setYear(cellDateVal.year); }
|
||
|
if(v.repeatMonth === true) { date.setMonth(cellDateVal.month); }
|
||
|
|
||
|
return date._val();
|
||
|
};
|
||
|
|
||
|
// Assign date for the cell
|
||
|
cell.html(cellDateVal.date);
|
||
|
|
||
|
// If we have selectable date ranges
|
||
|
if(options.selectableDateRange) {
|
||
|
isSelectable = false;
|
||
|
$.each(options.selectableDateRange, function(i, v) {
|
||
|
var dateFrom = v.from;
|
||
|
var dateTo = (v.to || null);
|
||
|
|
||
|
// If to is not specified, default to max days in the from month
|
||
|
dateTo = dateTo || new Date(v.from.getFullYear(), v.from.getMonth(), v.from._max());
|
||
|
|
||
|
// If repeating year, set the from and two to the current date's year
|
||
|
dateFrom = getRepeatDate(v, dateFrom);
|
||
|
dateTo = getRepeatDate(v, dateTo);
|
||
|
|
||
|
// Test to see if this date is selectable
|
||
|
if(cellDateTime >= dateFrom.time && cellDateTime <= dateTo.time) {
|
||
|
isSelectable = true;
|
||
|
return true;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Handle date ranges and collections
|
||
|
if(options.selectableDates) {
|
||
|
if((options.selectableDateRange && !isSelectable) || (isSelectable && !options.selectableDateRange)) {
|
||
|
isSelectable = false;
|
||
|
}
|
||
|
$.each(options.selectableDates, function(i, v) {
|
||
|
var vDate = getRepeatDate(v, v.date);
|
||
|
|
||
|
if(vDate.time == cellDateTime) {
|
||
|
return (isSelectable = true);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// If not active or if not within selectableMonths, set to noday otherwise evaluate accordingly
|
||
|
if(!isSelectable ||
|
||
|
selectableYears._indexOf(cellDateVal.year) < 0 ||
|
||
|
selectableMonths._indexOf(cellDateVal.month) < 0 ||
|
||
|
selectableDOW._indexOf(cellDateVal.day) < 0) {
|
||
|
cellClass = 'noday';
|
||
|
}
|
||
|
else {
|
||
|
// Handle active dates and weekends
|
||
|
cellClass = ([ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ])[cellDateVal.day];
|
||
|
|
||
|
// Handle today or selected dates
|
||
|
if(firstDateMonth != cellDateVal.month) { cellClass += ' outday'; }
|
||
|
if(todayTime == cellDateTime) { cellClass = 'today'; cellZIndex += 50; }
|
||
|
if(options.selectedDate._time() == cellDateTime) { cellClass = 'selected'; cellZIndex += 51; }
|
||
|
|
||
|
// Handle special dates
|
||
|
if(options.specialDates) {
|
||
|
$.each(options.specialDates, function(i, v) {
|
||
|
var vDate = getRepeatDate(v, v.date);
|
||
|
|
||
|
if(vDate.time == cellDateTime) {
|
||
|
cellClass = (v.cssClass || 'special');
|
||
|
cellZIndex += 52;
|
||
|
specialData = v.data;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
cell
|
||
|
.mousedown(function() { return false; })
|
||
|
.hover(function(e) {
|
||
|
e.stopPropagation();
|
||
|
|
||
|
// Get the data from this cell
|
||
|
var hoverData = $(this).data('data');
|
||
|
|
||
|
// Call callback
|
||
|
options.onHover(el, cell, hoverData.date, hoverData.data);
|
||
|
})
|
||
|
.click(function(e) {
|
||
|
e.stopPropagation();
|
||
|
|
||
|
// Get the data from this cell
|
||
|
var clickedData = $(this).data('data');
|
||
|
|
||
|
// Save date to selected and first
|
||
|
options.selectedDate = options.firstDate = clickedData.date;
|
||
|
|
||
|
// Update calendar (and auto-hide if necessary)
|
||
|
self.render(function() {
|
||
|
if(!options.showAlways && options.hideOnClick) {
|
||
|
self.hide();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Call callback
|
||
|
options.onClick(el, $(this), clickedData.date, clickedData.data);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update the css for the cell
|
||
|
$.extend(cellCSS,
|
||
|
{
|
||
|
borderTopWidth: borderSize,
|
||
|
borderBottomWidth: borderSize,
|
||
|
borderLeftWidth: (row > 0 || (!row && !col)) ? borderSize : 0,
|
||
|
borderRightWidth: (row > 0 || (!row && col == 6)) ? borderSize : 0,
|
||
|
marginLeft: (col > 0) ? '-' + (borderSize) : 0,
|
||
|
marginTop: (row > 0) ? '-' + (borderSize) : 0,
|
||
|
zIndex: cellZIndex
|
||
|
});
|
||
|
|
||
|
// Assign other properties to the cell
|
||
|
cell
|
||
|
.data('data', { date: cellDate, data: specialData})
|
||
|
.addClass(coreClass + cellClass)
|
||
|
.css(cellCSS);
|
||
|
|
||
|
// Add cell to calendar
|
||
|
calendar.append(cell);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Render the month / year title
|
||
|
|
||
|
// Helper function for toggling select and text
|
||
|
var toggleYearMonthSelect = function(showYear) {
|
||
|
var show = 'inline-block';
|
||
|
var hide = 'none';
|
||
|
|
||
|
if(options.allowMonthSelect) {
|
||
|
monthText.css({ display: !showYear ? hide : show });
|
||
|
monthSelect.css({ display: !showYear ? show : hide });
|
||
|
}
|
||
|
|
||
|
if(options.allowYearSelect) {
|
||
|
yearText.css({ display: showYear ? hide : show });
|
||
|
yearSelect.css({ display: showYear ? show : hide });
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Helper function when select is updated
|
||
|
var onYearMonthSelect = function() {
|
||
|
options.firstDate = new Date(yearSelect.val(), monthSelect.val(), 1);
|
||
|
self.render();
|
||
|
};
|
||
|
|
||
|
// Build month selector
|
||
|
var monthSelect = $('<select/>')
|
||
|
.hide()
|
||
|
.change(onYearMonthSelect);
|
||
|
|
||
|
// Build year selector
|
||
|
var yearSelect = $('<select/>')
|
||
|
.hide()
|
||
|
.change(onYearMonthSelect);
|
||
|
|
||
|
// Build month label
|
||
|
var monthText = $('<span/>')
|
||
|
.html(monthNames[firstDateMonth])
|
||
|
.mousedown(function() { return false; })
|
||
|
.click(function(e) {
|
||
|
e.stopPropagation();
|
||
|
toggleYearMonthSelect(false);
|
||
|
});
|
||
|
|
||
|
// Build year label
|
||
|
var yearText = $('<span/>')
|
||
|
.html(firstDateYear)
|
||
|
.mousedown(function() { return false; })
|
||
|
.click(function(e) {
|
||
|
e.stopPropagation();
|
||
|
toggleYearMonthSelect(true);
|
||
|
});
|
||
|
|
||
|
// Populate month select
|
||
|
$.each(monthNames, function(i, v) {
|
||
|
if(options.allowMonthSelect && selectableMonths._indexOf(i) != -1) {
|
||
|
var o = $('<option/>').html(v).attr('value', i);
|
||
|
if(i == firstDateMonth) { o.attr('selected', 'selected');}
|
||
|
monthSelect.append(o);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Populate year select
|
||
|
$.each(selectableYears, function(i, v) {
|
||
|
if(options.allowYearSelect) {
|
||
|
var o = $('<option/>').html(v).attr('value', v);
|
||
|
if(v == firstDateYear) { o.attr('selected', 'selected'); }
|
||
|
yearSelect.append(o);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var titleYearMonth = $('<div/>')
|
||
|
.append(monthText)
|
||
|
.append(monthSelect)
|
||
|
.append(yearText)
|
||
|
.append(yearSelect);
|
||
|
|
||
|
// Add to title
|
||
|
titleCell.children().remove();
|
||
|
titleCell.append(titleYearMonth);
|
||
|
|
||
|
// Run the callback signaling end of the render
|
||
|
renderCalback = renderCalback || (function() {});
|
||
|
renderCalback();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Return the plugin
|
||
|
return glDatePicker;
|
||
|
})();
|
||
|
|
||
|
// One time initialization of useful prototypes
|
||
|
(function() {
|
||
|
Date.prototype._clear = function() {
|
||
|
this.setHours(0);
|
||
|
this.setMinutes(0);
|
||
|
this.setSeconds(0);
|
||
|
this.setMilliseconds(0);
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
Date.prototype._time = function() {
|
||
|
return this._clear().getTime();
|
||
|
};
|
||
|
|
||
|
Date.prototype._max = function() {
|
||
|
var isLeapYear = (new Date(this.getYear(), 1, 29).getMonth() == 1) ? 1 : 0;
|
||
|
var days = [31, 28 + isLeapYear, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||
|
|
||
|
return days[this.getMonth()];
|
||
|
};
|
||
|
|
||
|
Date.prototype._add = function(days) {
|
||
|
this.setDate(this.getDate() + days);
|
||
|
};
|
||
|
|
||
|
Date.prototype._first = function() {
|
||
|
var date = new Date(this);
|
||
|
date.setDate(1);
|
||
|
|
||
|
return date;
|
||
|
};
|
||
|
|
||
|
Date.prototype._val = function() {
|
||
|
this._clear();
|
||
|
|
||
|
return {
|
||
|
year: this.getFullYear(),
|
||
|
month: this.getMonth(),
|
||
|
date: this.getDate(),
|
||
|
time: this.getTime(),
|
||
|
day: this.getDay()
|
||
|
};
|
||
|
};
|
||
|
|
||
|
Array.prototype._indexOf = function(value) {
|
||
|
return $.inArray(value, this);
|
||
|
}
|
||
|
})();
|
||
|
})();
|