Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mckabue/262e0684c5947e8624988028e1bc93b3 to your computer and use it in GitHub Desktop.
Save mckabue/262e0684c5947e8624988028e1bc93b3 to your computer and use it in GitHub Desktop.
+this is an lightweight, simple, extensible jQuery plugin that just outputs a calendar the way a developer wants. it supports templating, adding events, in short, THIS IS THE PLUGIN YOU REFER AND/OR EXTEND WHEN YOU WANT TO CREATE A FULLY GROWN CALENDAR
ko.bindingHandlers.kocalender = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor(),
instanceOptions = $.extend({
previousYearSelector: ".previousyear",
nextYearSelector: ".nextyear",
previousMonthSelector: ".previousmonth",
nextMonthSelector: ".nextmonth",
eventTemplate: '<div data-id="{{id}}" class="event">{{name}}</div>',
dateCallbacks: [
{
event: 'click', on: ["td .event"], func: function (e) {
var $this = $(this),
$data = $this.data();
alert($data.id);
datainterface.showModal({
viewModel: {
cancel: function () {
// Close the modal without passing any result data.
this.modal.close();
},
id: $data.id
},
template: 'doctor-view-schedules-modal',
context: viewModel // Set context so we don't need to bind the callback function
});
}
}
]
}, value.instanceOptions || {}),
eventOptions = $.extend({
}, value.eventOptions || {}),
events = ko.utils.unwrapObservable(value.events || []),
$element = $(element),
instance = $element.calender(instanceOptions);
return { 'controlsDescendantBindings': true };
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var $element = $(element),
value = valueAccessor(),
eventOptions = $.extend({
}, value.eventOptions || {}),
events = ko.utils.unwrapObservable(value.events || []),
instance = $element.data('calender');
instance.addEvents(events.map(function (item) {
return $.extend(item, eventOptions);
}));
}
};
//Another Good Plugin Pattern :: https://alexsexton.com/blog/2010/02/using-inheritance-patterns-to-organize-large-jquery-applications/
$.fn.calender = function (_options) {
var Calender = function (element, options) {
var _defaults = $.extend({
year: this.currentYear(),
month: this.currentMonth(),
template: this.calendarTemplate,
dateCallbacks: [],
previousYearSelector: "",
nextYearSelector: "",
previousMonthSelector: "",
nextMonthSelector: "",
eventTemplate: ""
}, options);
this.workingYear = _defaults.year;
this.workingMonth = _defaults.month;
this.dateCallbacks = _defaults.dateCallbacks;
this.previousYearSelector = _defaults.previousYearSelector;
this.nextYearSelector = _defaults.nextYearSelector;
this.previousMonthSelector = _defaults.previousMonthSelector;
this.nextMonthSelector = _defaults.nextMonthSelector;
this.eventTemplate = _defaults.eventTemplate;
this.$selector = $(element);
this.datesInMonth();
};
Calender.prototype.dateCallbacks = [];
Calender.prototype.previousYearSelector = "";
Calender.prototype.nextYearSelector = "";
Calender.prototype.previousMonthSelector = "";
Calender.prototype.nextMonthSelector = "";
Calender.prototype.$selector = "";
Calender.prototype.get = function (get) {
return this[get]();
};
Calender.prototype.currentDate = new Date();
Calender.prototype.currentYear = function () {
return this.currentDate.getFullYear();
};
Calender.prototype.currentMonth = function () {
return this.currentDate.getMonth();
};
Calender.prototype.currentDay = function () {
return this.currentDate.getDay();
};
Calender.prototype.currentHour = function () {
return 1;
};
Calender.prototype.currentMinute = function () {
return 1;
};
Calender.prototype.workingYear = 0;
Calender.prototype.workingMonth = 0;
Calender.prototype.workingNextMonthYear = 0;
Calender.prototype.workingNextMonthMonth = 0;
Calender.prototype.workingPreviousMonthYear = 0;
Calender.prototype.workingPreviousMonthMonth = 0;
Calender.prototype.workingDates = [];
Calender.prototype.daysLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; // these are labels for the dates of the week
Calender.prototype.monthsLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; // these are human-readable month name labels, in order
Calender.prototype.workingMonthLabel = function () {
return this.monthsLabels[this.workingMonth];
};
Calender.prototype.datesInMonth = function (month, year) {
var _month = (isNaN(month) || month == null) ? this.workingMonth : month,
_year = (isNaN(year) || year == null) ? this.workingYear : year,
thisMonth = new Date(_year, _month, 1),
thisMonthLength = getMonthLength(_month, _year),
positionOfFirstDayOfThisMonth = thisMonth.getDay(),
lastMonth = new Date(_year, _month, 0),
_lastMonth = lastMonth.getMonth(),
_lastMonthYear = lastMonth.getFullYear(),
lastMonthLength = getMonthLength(_lastMonth, _lastMonthYear),
positionOfLastDayOfLastMonth = lastMonth.getDay(),
nextMonth = new Date(_year, _month, (thisMonthLength + 1)),
_nextMonth = nextMonth.getMonth(),
_nextMonthYear = nextMonth.getFullYear(),
nextMonthLength = getMonthLength(_nextMonth, _nextMonthYear),
positionOfFirstDayOfNextMonth = nextMonth.getDay();
var dates1 = getDates(lastMonthLength, (lastMonthLength - lastMonthPositionStart(positionOfLastDayOfLastMonth)), lastMonthLength);
var dates2 = getDates(thisMonthLength, 0, thisMonthLength);
var dates3 = getDates(nextMonthLength, 0, (42 - (dates1.length + dates2.length)));
var dates = dates1.map(function (item) {
return {
date: item,
month: _lastMonth,
year: _lastMonthYear
};
}).concat(dates2.map(function (item) {
return {
date: item,
month: _month,
year: _year
};
})).concat(
dates3.map(function (item) {
return {
date: item,
month: _nextMonth,
year: _nextMonthYear
};
}));
this.workingMonthWeeks = convertDatesIntoWeeks(dates);
this.workingNextMonthYear = _nextMonthYear;
this.workingNextMonthMonth = _nextMonth;
this.workingPreviousMonthYear = _lastMonthYear;
this.workingPreviousMonthMonth = _lastMonth;
this.generateCalendar();
this.displayCalendar();
};
Calender.prototype.calendarHtml = "";
Calender.prototype.calendarTemplate =
'<table class="table table-bordered">' +
'<thead>' +
'<tr>' +
'<th class="col-md-4" colspan="4"> <button class="previousyear">Previous Year</button> {{ workingYear }} <button class="nextyear">Next Year</button>' +
'<th class="col-md-7" colspan="3"> <button class="previousmonth">Previous Month</button> {{ workingMonthLabel }} <button class="nextmonth">Next Month</button></th>' +
'</tr>' +
'<tr>' +
'{{ for(var day in daysLabels) { }}' +
'<th class="col-md-1">{{ daysLabels[day] }}</th>' +
'{{ } }}' +
'</tr>' +
'</thead>' +
'<tbody>' +
'{{ for(var week in workingMonthWeeks) { }}' +
'<tr>' +
'{{ for(var date in workingMonthWeeks[week].dates) { }}' +
'{{ if(workingMonthWeeks[week].dates[date].month === workingMonth) { }}' +
'<td class="col-md-1" data-year="{{workingMonthWeeks[week].dates[date].year}}" data-month="{{workingMonthWeeks[week].dates[date].month}}" data-date="{{workingMonthWeeks[week].dates[date].date}}">' +
'{{workingMonthWeeks[week].dates[date].date}} ' +
'</td>' +
'{{ } }}' +
'{{ else { }}>' +
'<td class="col-md-1 not-in-month" data-year="{{workingMonthWeeks[week].dates[date].year}}" data-month="{{workingMonthWeeks[week].dates[date].month}}" data-date="{{workingMonthWeeks[week].dates[date].date}}">{{workingMonthWeeks[week].dates[date].date}}</td>' +
'{{ } }}' +
'{{ } }}' +
'</tr>' +
'{{ } }}' +
'</tbody>' +
'</table>';
Calender.prototype.generateCalendar = function () {
var html = template(this.calendarTemplate, {
daysLabels: this.daysLabels,
workingYear: this.workingYear,
workingMonthLabel: this.workingMonthLabel(),
workingMonth: this.workingMonth,
workingMonthWeeks: this.workingMonthWeeks
});
this.calendarHtml = html;
};
Calender.prototype.displayCalendar = function () {
var self = this;
self.$selector.off();
self.$selector.html(self.calendarHtml);
self.updateEvents();
$.each(self.dateCallbacks, function (i, item) {
self.$selector.on(item.event, item.on.join(','), item.func);
});
self.$selector.one('click', self.previousYearSelector, function (e) {
// e.stopPropagation()
self.previousYear();
});
self.$selector.one('click', self.nextYearSelector, function (e) {
// e.stopPropagation()
self.nextYear();
});
self.$selector.one('click', self.previousMonthSelector, function (e) {
self.previousMonth();
});
self.$selector.one('click', self.nextMonthSelector, function (e) {
self.nextMonth();
});
};
Calender.prototype.nextYear = function () {
this.workingYear++;
this.datesInMonth();
};
Calender.prototype.nextMonth = function () {
this.workingYear = this.workingNextMonthYear;
this.workingMonth = this.workingNextMonthMonth;
this.datesInMonth();
};
Calender.prototype.previousYear = function () {
this.workingYear--;
this.datesInMonth();
};
Calender.prototype.previousMonth = function () {
this.workingYear = this.workingPreviousMonthYear;
this.workingMonth = this.workingPreviousMonthMonth;
this.datesInMonth();
};
Calender.prototype.currentEvents = [];
Calender.prototype.eventTemplate = "";
Calender.prototype.addEvents = function (events) {
this.currentEvents = this.currentEvents.concat(events);
this.updateEvents();
};
Calender.prototype.updateEvents = function (events) {
var self = this;
self.currentEvents = events || self.currentEvents;
$.each(self.currentEvents, function (i, item) {
var _$event = utils.template(self.eventTemplate, item );
self.$selector.find(item.singleDateSelector.join(',')).append(_$event);
});
};
function convertDatesIntoWeeks(dates) {
var weeks = [],
weeksLength = (dates.length / 7),
datesLength = 6,
dateIndex = 0,
weekIndex = 1;
// this loop is for is weeks (rows)
for (weekIndex; weekIndex <= weeksLength; weekIndex++) {
var _week = [];
// this loop is for weekdates (cells)
for (dateIndex; dateIndex <= datesLength; dateIndex++) {
_week.push({
date: dates[dateIndex].date,
month: dates[dateIndex].month,
year: dates[dateIndex].year
});
}
datesLength = (7 + datesLength);
weeks.push({
dates: _week
});
}
return weeks;
};
function nextMonthPositionEnd(index) {
return 7 - index;
};
function lastMonthPositionStart(index) {
if (index === 6)
return 0;
else
return index + 1;
};
function getDates(allDates, start, end) {
var dates = [];
for (var i = 1; i <= allDates; i++) {
dates.push(i);
}
return dates.slice(start, end);
};
function getMonthLength(_month, _year) {
var dates_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], // these are the dates of the week for each month, in order
monthLength = dates_in_month[_month];
// compensate for leap year
if (_month == 1) { // February only!
if ((_year % 4 == 0 && _year % 100 != 0) || _year % 400 == 0) {
monthLength = 29;
}
}
return monthLength;
}
///http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line
function template(html, options) {
var re = /\{\{(.+?)\}\}/g,
reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g,
code = 'with(obj) { var r=[];\n',
cursor = 0,
result,
match;
var add = function (line, js) {
js ? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
(code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
return add;
}
while (match = re.exec(html)) {
add(html.slice(cursor, match.index))(match[1], true);
cursor = match.index + match[0].length;
}
add(html.substr(cursor, html.length - cursor));
code = (code + 'return r.join(""); }').replace(/[\r\t\n]/g, ' ');
try { result = new Function('obj', code).apply(options, [options]); }
catch (err) { console.error("'" + err.message + "'", " in \n\nCode:\n", code, "\n"); }
return result;
};
var instance = new Calender(this[0], _options);
instance.$selector.data('calender', instance);
return instance;
};
// OPTIONS CURRENTLY AVAILABLE
//{
// previousYearSelector: ".previousyear",
// nextYearSelector: ".nextyear",
// previousMonthSelector: ".previousmonth",
// nextMonthSelector: ".nextmonth",
// eventTemplate: '<div data-id="{{id}}" class="event">{{name}}</div>',
// dateCallbacks: [
// {
// event: 'click', on: ["td .event"], func: function (e) {
// var $this = $(this),
// $data = $this.data();
// alert($data.id);
// datainterface.showModal({
// viewModel: {
// cancel: function () {
// // Close the modal without passing any result data.
// this.modal.close();
// },
// id: $data.id
// },
// template: 'doctor-view-schedules-modal',
// context: viewModel // Set context so we don't need to bind the callback function
// });
// }
// }
// ]
//}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment