lib/plugins/calendar/js/calendar.min.js
/**
* Bootstrap based calendar full view.
*
* https://github.com/Serhioromano/bootstrap-calendar
*
* User: Sergey Romanov <serg4172@mail.ru>
*
* ****
* Edited by Cunity to display only month view without event-list
*/
"use strict";
Date.prototype.getWeek = function() {
var onejan = new Date(this.getFullYear(), 0, 1);
return Math.ceil((((this.getTime() - onejan.getTime()) / 86400000) + onejan.getDay() + 1) / 7);
};
Date.prototype.getMonthFormatted = function() {
var month = this.getMonth() + 1;
return month < 10 ? '0' + month : month;
};
Date.prototype.getDateFormatted = function() {
var date = this.getDate();
return date < 10 ? '0' + date : date;
};
if (!String.prototype.format) {
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
};
}
if (!String.prototype.formatNum) {
String.prototype.formatNum = function(decimal) {
var r = "" + this;
while (r.length < decimal)
r = "0" + r;
return r;
};
}
(function($) {
var defaults = {
// Width of the calendar
width: '100%',
// Initial view (can be 'month', 'week', 'day')
view: 'month',
// Initial date. No matter month, week or day this will be a starting point. Can be 'now' or a date in format 'yyyy-mm-dd'
day: 'now',
// Day Start time and end time with time intervals. Time split 10, 15 or 30.
time_start: '06:00',
time_end: '22:00',
time_split: '30',
// Source of events data. It can be one of the following:
// - URL to return JSON list of events in special format.
// {success:1, result: [....]} or for error {success:0, error:'Something terrible happened'}
// events: [...] as described in events property description
// The start and end variables will be sent to this url
// - A function that received the start and end date, and that
// returns an array of events (as described in events property description)
// - An array containing the events
events_source: '',
// Path to templates should end with slash /. It can be as relative
// /component/bootstrap-calendar/tmpls/
// or absolute
// http://localhost/component/bootstrap-calendar/tmpls/
tmpl_path: 'tmpls/',
tmpl_cache: true,
classes: {
months: {
inmonth: 'cal-day-inmonth',
outmonth: 'cal-day-outmonth',
saturday: 'cal-day-weekend',
sunday: 'cal-day-weekend',
today: 'cal-day-today'
},
week: {
workday: 'cal-day-workday',
saturday: 'cal-day-weekend',
sunday: 'cal-day-weekend',
today: 'cal-day-today'
}
},
views: {
month: {
slide_events: 1,
enable: 1
}
},
// ------------------------------------------------------------
// CALLBACKS. Events triggered by calendar class. You can use
// those to affect you UI
// ------------------------------------------------------------
onAfterEventsLoad: function(events) {
// Inside this function 'this' is the calendar instance
},
onBeforeEventsLoad: function(next) {
// Inside this function 'this' is the calendar instance
next();
},
onAfterViewLoad: function(view) {
// Inside this function 'this' is the calendar instance
},
// -------------------------------------------------------------
// INTERNAL USE ONLY. DO NOT ASSIGN IT WILL BE OVERRIDDEN ANYWAY
// -------------------------------------------------------------
events: [],
templates: {
year: '',
month: '',
week: '',
day: ''
},
stop_cycling: false
};
var defaults_extended = {
first_day: 2
};
var strings = {
error_noview: 'Calendar: View {0} not found',
error_dateformat: 'Calendar: Wrong date format {0}. Should be either "now" or "yyyy-mm-dd"',
error_loadurl: 'Calendar: Event URL is not set',
error_where: 'Calendar: Wrong navigation direction {0}. Can be only "next" or "prev" or "today"',
error_timedevide: 'Calendar: Time split parameter should divide 60 without decimals. Something like 10, 15, 30',
no_events_in_day: 'No events in this day.',
title_year: '{0}',
title_month: '{0} {1}',
title_week: 'week {0} of {1}',
title_day: '{0} {1} {2}, {3}',
week: 'Week {0}',
all_day: 'All day',
time: 'Time',
events: 'Events',
before_time: 'Ends before timeline',
after_time: 'Starts after timeline',
m0: 'January',
m1: 'February',
m2: 'March',
m3: 'April',
m4: 'May',
m5: 'June',
m6: 'July',
m7: 'August',
m8: 'September',
m9: 'October',
m10: 'November',
m11: 'December',
ms0: 'Jan',
ms1: 'Feb',
ms2: 'Mar',
ms3: 'Apr',
ms4: 'May',
ms5: 'Jun',
ms6: 'Jul',
ms7: 'Aug',
ms8: 'Sep',
ms9: 'Oct',
ms10: 'Nov',
ms11: 'Dec',
d0: 'Sunday',
d1: 'Monday',
d2: 'Tuesday',
d3: 'Wednesday',
d4: 'Thursday',
d5: 'Friday',
d6: 'Saturday'
};
var browser_timezone = '';
try {
if ($.type(window.jstz) == 'object' && $.type(jstz.determine) == 'function') {
browser_timezone = jstz.determine().name();
if ($.type(browser_timezone) !== 'string') {
browser_timezone = '';
}
}
}
catch (e) {
}
function buildEventsUrl(events_url, data) {
var separator, key, url;
url = events_url;
separator = (events_url.indexOf('?') < 0) ? '?' : '&';
for (key in data) {
url += separator + key + '=' + encodeURIComponent(data[key]);
separator = '&';
}
return url;
}
function getExtentedOption(cal, option_name) {
var fromOptions = (cal.options[option_name] != null) ? cal.options[option_name] : null;
var fromLanguage = (cal.locale[option_name] != null) ? cal.locale[option_name] : null;
if (fromOptions != null) {
return fromOptions;
}
if (fromLanguage != null) {
return fromLanguage;
}
return defaults_extended[option_name];
}
function warn(message) {
if ($.type(window.console) == 'object' && $.type(window.console.warn) == 'function') {
window.console.warn('[Bootstrap-Calendar] ' + message);
}
}
function Calendar(params, context) {
this.options = $.extend(true, {position: {start: new Date(), end: new Date()}}, defaults, params);
this.setLanguage(this.options.language);
this.context = context;
context.css('width', this.options.width).addClass('cal-context');
this.view();
return this;
}
Calendar.prototype.setOptions = function(object) {
$.extend(this.options, object);
if ('language' in object) {
this.setLanguage(object.language);
}
}
Calendar.prototype.setLanguage = function(lang) {
if (window.calendar_languages && (lang in window.calendar_languages)) {
this.locale = $.extend(true, {}, strings, calendar_languages[lang]);
this.options.language = lang;
} else {
this.locale = strings;
delete this.options.language;
}
}
Calendar.prototype._render = function() {
this.context.html('');
this._loadTemplate(this.options.view);
this.stop_cycling = false;
var data = {};
data.cal = this;
data.day = 1;
// Getting list of days in a week in correct order. Works for month and week views
if (getExtentedOption(this, 'first_day') == 1) {
data.months = [this.locale.d1, this.locale.d2, this.locale.d3, this.locale.d4, this.locale.d5, this.locale.d6, this.locale.d0]
} else {
data.months = [this.locale.d0, this.locale.d1, this.locale.d2, this.locale.d3, this.locale.d4, this.locale.d5, this.locale.d6]
}
// Get all events between start and end
var start = parseInt(this.options.position.start.getTime());
var end = parseInt(this.options.position.end.getTime());
data.events = this.getEventsBetween(start, end);
data.start = new Date(this.options.position.start.getTime());
data.lang = this.locale;
this.context.append(this.options.templates[this.options.view](data));
this._update();
};
Calendar.prototype._month = function(month) {
this._loadTemplate('year-month');
var t = {cal: this};
var newmonth = month + 1;
t.data_day = this.options.position.start.getFullYear() + '-' + (newmonth < 10 ? '0' + newmonth : newmonth) + '-' + '01';
t.month_name = this.locale['m' + month];
var curdate = new Date(this.options.position.start.getFullYear(), month, 1, 0, 0, 0);
t.start = parseInt(curdate.getTime());
t.end = parseInt(new Date(this.options.position.start.getFullYear(), month + 1, 1, 0, 0, 0).getTime());
t.events = this.getEventsBetween(t.start, t.end);
return this.options.templates['year-month'](t);
}
Calendar.prototype._day = function(week, day) {
this._loadTemplate('month-day');
var t = {tooltip: '', cal: this};
var cls = this.options.classes.months.outmonth;
var firstday = this.options.position.start.getDay();
if (getExtentedOption(this, 'first_day') == 2) {
firstday++;
} else {
firstday = (firstday == 0 ? 7 : firstday);
}
day = (day - firstday) + 1;
var curdate = new Date(this.options.position.start.getFullYear(), this.options.position.start.getMonth(), day, 0, 0, 0);
// if day of the current month
if (day > 0) {
cls = this.options.classes.months.inmonth;
}
// stop cycling table rows;
var daysinmonth = (new Date(this.options.position.end.getTime() - 1)).getDate();
if ((day + 1) > daysinmonth) {
this.stop_cycling = true;
}
// if day of the next month
if (day > daysinmonth) {
day = day - daysinmonth;
cls = this.options.classes.months.outmonth;
}
cls = $.trim(cls + " " + this._getDayClass("months", curdate));
if (day <= 0) {
var daysinprevmonth = (new Date(this.options.position.start.getFullYear(), this.options.position.start.getMonth(), 0)).getDate();
day = daysinprevmonth - Math.abs(day);
cls += ' cal-month-first-row';
}
t.data_day = curdate.getFullYear() + '-' + curdate.getMonthFormatted() + '-' + (day < 10 ? '0' + day : day);
t.cls = cls;
t.day = day;
t.start = parseInt(curdate.getTime());
t.end = parseInt(t.start + 86400000);
t.events = this.getEventsBetween(t.start, t.end);
return this.options.templates['month-day'](t);
}
Calendar.prototype._getDayClass = function(class_group, date) {
var self = this;
var addClass = function(which, to) {
var cls;
cls = (self.options.classes && (class_group in self.options.classes) && (which in self.options.classes[class_group])) ? self.options.classes[class_group][which] : "";
if ((typeof (cls) == "string") && cls.length) {
to.push(cls);
}
};
var classes = [];
if (date.toDateString() == (new Date()).toDateString()) {
addClass("today", classes);
}
switch (date.getDay()) {
case 0:
addClass("sunday", classes);
break;
case 6:
addClass("saturday", classes);
break;
}
addClass(date.toDateString(), classes);
return classes.join(" ");
};
Calendar.prototype.view = function(view) {
this._init_position();
this._loadEvents();
this._render();
this.options.onAfterViewLoad.call(this, this.options.view);
};
Calendar.prototype.navigate = function(where, next) {
var to = $.extend({}, this.options.position);
if (where == 'next')
to.start.setMonth(this.options.position.start.getMonth() + 1);
else if (where == 'prev')
to.start.setMonth(this.options.position.start.getMonth() - 1);
else if (where == 'today')
to.start.setTime(new Date().getTime());
else
$.error(this.locale.error_where.format(where))
this.options.day = to.start.getFullYear() + '-' + to.start.getMonthFormatted() + '-' + to.start.getDateFormatted();
this.view();
if (_.isFunction(next))
next();
};
Calendar.prototype._init_position = function() {
var year, month, day;
if (this.options.day == 'now') {
var date = new Date();
year = date.getFullYear();
month = date.getMonth();
day = date.getDate();
} else if (this.options.day.match(/^\d{4}-\d{2}-\d{2}$/g)) {
var list = this.options.day.split('-');
year = parseInt(list[0], 10);
month = parseInt(list[1], 10) - 1;
day = parseInt(list[2], 10);
}
else {
$.error(this.locale.error_dateformat.format(this.options.day));
}
this.options.position.start.setTime(new Date(year, month, 1).getTime());
this.options.position.end.setTime(new Date(year, month + 1, 1).getTime());
return this;
};
Calendar.prototype.getTitle = function() {
var p = this.options.position.start;
return this.locale.title_month.format(this.locale['m' + p.getMonth()], p.getFullYear());
};
Calendar.prototype.isToday = function() {
var now = new Date().getTime();
return ((now > this.options.position.start) && (now < this.options.position.end));
}
Calendar.prototype.getStartDate = function() {
return this.options.position.start;
}
Calendar.prototype.getEndDate = function() {
return this.options.position.end;
}
Calendar.prototype._loadEvents = function() {
var self = this;
var source = null;
if ('events_source' in this.options && this.options.events_source !== '') {
source = this.options.events_source;
}
else if ('events_url' in this.options) {
source = this.options.events_url;
warn('The events_url option is DEPRECATED and it will be REMOVED in near future. Please use events_source instead.');
}
var loader;
if (source.length) {
loader = function() {
var events = [];
var params = {from: self.options.position.start.getTime(), to: self.options.position.end.getTime()};
if (browser_timezone.length) {
params.browser_timezone = browser_timezone;
}
$.ajax({
url: buildEventsUrl(source, params),
dataType: 'json',
type: 'GET',
async: false
}).done(function(json) {
if (!json.success) {
$.error(json.error);
}
if (json.result) {
for (var i = 0; i < json.result.length; i++) {
json.result[i].time = moment(parseInt(json.result[i].start));
}
events = json.result;
}
});
return events;
};
}
if (!loader) {
$.error(this.locale.error_loadurl);
}
this.options.onBeforeEventsLoad.call(this, function() {
self.options.events = loader();
self.options.events.sort(function(a, b) {
var delta;
delta = a.start - b.start;
if (delta == 0) {
delta = a.end - b.end;
}
return delta;
});
self.options.onAfterEventsLoad.call(self, self.options.events);
});
};
Calendar.prototype._loadTemplate = function(name) {
if (this.options.templates[name]) {
return;
}
var self = this;
$.ajax({
url: this.options.tmpl_path + name + '.html',
dataType: 'html',
type: 'GET',
async: false,
cache: this.options.tmpl_cache
}).done(function(html) {
self.options.templates[name] = _.template(html);
});
};
Calendar.prototype._update = function() {
var self = this;
$('*[data-toggle="tooltip"]').tooltip({container: 'body'});
this['_update_' + this.options.view]();
};
Calendar.prototype._update_month = function() {
var self = this;
$('a.event').mouseenter(function() {
$('a[data-event-id="' + $(this).data('event-id') + '"]').closest('.cal-cell1').addClass('day-highlight dh-' + $(this).data('event-class'));
});
$('a.event').mouseleave(function() {
$('div.cal-cell1').removeClass('day-highlight dh-' + $(this).data('event-class'));
});
};
Calendar.prototype.getEventsBetween = function(start, end) {
var events = [];
$.each(this.options.events, function() {
if ((parseInt(this.start) < end || this.start == null) && (parseInt(this.end) >= start || this.end == null)) {
events.push(this);
}
});
return events;
};
$.fn.calendar = function(params) {
return new Calendar(params, this);
}
}(jQuery));