// the semi-colon before function invocation is a safety net against concatenated // scripts and/or other plugins which may not be closed properly. ;(function ($, window, document, undefined) { "use strict"; // Create the defaults once var pluginName = "simpleCalendar", defaults = { months: ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'], //string of months starting from january days: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'], //string of days starting from sunday displayYear: true, // display year in header fixedStartDay: true, // Week begin always by monday or by day set by number 0 = sunday, 7 = saturday, false = month always begin by first day of the month displayEvent: true, // display existing event disableEventDetails: false, // disable showing event details disableEmptyDetails: false, // disable showing empty date details events: [], // List of event onInit: function (calendar) {}, // Callback after first initialization onMonthChange: function (month, year) {}, // Callback on month change onDateSelect: function (date, events) {}, // Callback on date selection onEventSelect: function () {}, // Callback fired when an event is selected - see $(this).data('event') onEventCreate: function( $el ) {}, // Callback fired when an HTML event is created - see $(this).data('event') onDayCreate: function( $el, d, m, y ) {} // Callback fired when an HTML day is created - see $(this).data('today'), .data('todayEvents') }; // The actual plugin constructor function Plugin(element, options) { this.element = element; this.settings = $.extend({}, defaults, options); this._defaults = defaults; this._name = pluginName; this.currentDate = new Date(); this.init(); } // Avoid Plugin.prototype conflicts $.extend(Plugin.prototype, { init: function () { var container = $(this.element); var todayDate = this.currentDate; var calendar = $('
'); var header = $('
' + '

' + '' + '' + '
'); this.updateHeader(todayDate, header); calendar.append(header); this.buildCalendar(todayDate, calendar); container.append(calendar); this.bindEvents(); this.settings.onInit(this); }, //Update the current month header updateHeader: function (date, header) { var monthText = this.settings.months[date.getMonth()]; monthText += this.settings.displayYear ? '
' + date.getFullYear() : '
'; header.find('.month').html(monthText); }, //Build calendar of a month from date buildCalendar: function (fromDate, calendar) { var plugin = this; calendar.find('table').remove(); var body = $('
'); var thead = $(''); var tbody = $(''); //setting current year and month var y = fromDate.getFullYear(), m = fromDate.getMonth(); //first day of the month var firstDay = new Date(y, m, 1); //last day of the month var lastDay = new Date(y, m + 1, 0); // Start day of weeks var startDayOfWeek = firstDay.getDay(); if (this.settings.fixedStartDay !== false) { // Backward compatibility startDayOfWeek = this.settings.fixedStartDay === true ? 1 : this.settings.fixedStartDay; // If first day of month is different of startDayOfWeek while (firstDay.getDay() !== startDayOfWeek) { firstDay.setDate(firstDay.getDate() - 1); } // If last day of month is different of startDayOfWeek + 7 while (lastDay.getDay() !== ((startDayOfWeek + 6) % 7)) { lastDay.setDate(lastDay.getDate() + 1); } } //Header day in a week ( (x to x + 7) % 7 to start the week by monday if x = 1) for (var i = startDayOfWeek; i < startDayOfWeek + 7; i++) { thead.append($('' + this.settings.days[i % 7].substring(0, 3) + '')); } //For firstDay to lastDay for (var day = firstDay; day <= lastDay; day.setDate(day.getDate())) { var tr = $(''); //For each row for (var i = 0; i < 7; i++) { var td = $('
' + day.getDate() + '
'); var $day = td.find('.day'); //if today is this day if (day.toDateString() === (new Date).toDateString()) { $day.addClass("today"); } //if day is not in this month if (day.getMonth() != fromDate.getMonth()) { $day.addClass("wrong-month"); } // filter today's events var todayEvents = plugin.getDateEvents(day); if (todayEvents.length && plugin.settings.displayEvent) { $day.addClass(plugin.settings.disableEventDetails ? "has-event disabled" : "has-event"); } else { $day.addClass(plugin.settings.disableEmptyDetails ? "disabled" : ""); } // associate some data available from the onDayCreate callback $day.data( 'todayEvents', todayEvents ); // simplify further customization this.settings.onDayCreate( $day, day.getDate(), m, y ); tr.append(td); day.setDate(day.getDate() + 1); } tbody.append(tr); } body.append(thead); body.append(tbody); var eventContainer = $('
'); calendar.append(body); calendar.append(eventContainer); }, changeMonth: function (value) { this.currentDate.setMonth(this.currentDate.getMonth() + value, 1); this.buildCalendar(this.currentDate, $(this.element).find('.calendar')); this.updateHeader(this.currentDate, $(this.element).find('.calendar header')); this.settings.onMonthChange(this.currentDate.getMonth(), this.currentDate.getFullYear()) }, //Init global events listeners bindEvents: function () { var plugin = this; //Remove previously created events $(plugin.element).off(); //Click previous month $(plugin.element).on('click', '.btn-prev', function ( e ) { plugin.changeMonth(-1) e.preventDefault(); }); //Click next month $(plugin.element).on('click', '.btn-next', function ( e ) { plugin.changeMonth(1); e.preventDefault(); }); //Binding day event $(plugin.element).on('click', '.day', function (e) { var date = new Date($(this).data('date')); var events = plugin.getDateEvents(date); if (!$(this).hasClass('disabled')) { plugin.fillUp(e.pageX, e.pageY); plugin.displayEvents(events); } plugin.settings.onDateSelect(date, events); }); //Binding event container close $(plugin.element).on('click', '.event-container .close', function (e) { plugin.empty(e.pageX, e.pageY); }); }, displayEvents: function (events) { var plugin = this; var container = $(this.element).find('.event-wrapper'); events.forEach(function (event) { var startDate = new Date(event.startDate); var endDate = new Date(event.endDate); var $event = $('' + '
' + '
' + startDate.getHours() + ':' + (startDate.getMinutes() < 10 ? '0' : '') + startDate.getMinutes() + '
' + '
' + plugin.formatDateEvent(startDate, endDate) + '
' + '
' + event.summary + '
' + '
'); $event.data( 'event', event ); $event.click( plugin.settings.onEventSelect ); // simplify further customization plugin.settings.onEventCreate( $event ); container.append($event); }) }, addEvent: function(newEvent) { var plugin = this; // add the new event to events list plugin.settings.events = [...plugin.settings.events, newEvent] this.buildCalendar(this.currentDate, $(this.element).find('.calendar')); }, setEvents: function(newEvents) { var plugin = this; // add the new event to events list plugin.settings.events = newEvents this.buildCalendar(this.currentDate, $(this.element).find('.calendar')); }, //Small effect to fillup a container fillUp: function (x, y) { var plugin = this; var elem = $(plugin.element); var elemOffset = elem.offset(); var filler = $('
'); filler.css("left", x - elemOffset.left); filler.css("top", y - elemOffset.top); elem.find('.calendar').append(filler); filler.animate({ width: "300%", height: "300%" }, 500, function () { elem.find('.event-container').show(); filler.hide(); }); }, //Small effect to empty a container empty: function (x, y) { var plugin = this; var elem = $(plugin.element); var elemOffset = elem.offset(); var filler = elem.find('.filler'); filler.css("width", "300%"); filler.css("height", "300%"); filler.show(); elem.find('.event-container').hide().find('.event').remove(); filler.animate({ width: "0%", height: "0%" }, 500, function () { filler.remove(); }); }, getDateEvents: function (d) { var plugin = this; return plugin.settings.events.filter(function (event) { return plugin.isDayBetween(new Date(d), new Date(event.startDate), new Date(event.endDate)); }); }, isDayBetween: function (d, dStart, dEnd) { dStart.setHours(0,0,0); dEnd.setHours(23,59,59,999); d.setHours(12,0,0); return dStart <= d && d <= dEnd; }, formatDateEvent: function (dateStart, dateEnd) { var formatted = ''; formatted += this.settings.days[dateStart.getDay()] + ' - ' + dateStart.getDate() + ' ' + this.settings.months[dateStart.getMonth()].substring(0, 3); if (dateEnd.getDate() !== dateStart.getDate()) { formatted += ' to ' + dateEnd.getDate() + ' ' + this.settings.months[dateEnd.getMonth()].substring(0, 3) } return formatted; } }); // A really lightweight plugin wrapper around the constructor, // preventing against multiple instantiations $.fn[pluginName] = function (options) { return this.each(function () { if (!$.data(this, "plugin_" + pluginName)) { $.data(this, "plugin_" + pluginName, new Plugin(this, options)); } }); }; })(jQuery, window, document);