');
- } else {
- console.error('Monthly.js has an incorrect entry for the weekStart variable');
- }
+ _appendDayNames(weekStartsOnMonday);
+
+ // Add CSS classes for the primary language and the locale. This allows for CSS-driven
+ // overrides of the language-specific header buttons. Lowercased because locale codes
+ // are case-insensitive but CSS is not.
+ $(parent).addClass("monthly-locale-" + primaryLanguageCode + " monthly-locale-" + locale);
// Add Header & event list markup
- $('#' + uniqueId).prepend('
').append('');
+ $(parent).prepend('
').append('');
+
+ // Set the calendar the first time
+ setMonthly(currentMonth, currentYear);
// How many days are in this month?
- function daysInMonth(m, y){
- return m===2?y&3||!(y%25)&&y&15?28:29:30+(m+(m>>3)&1);
+ function daysInMonth(month, year) {
+ return month === 2 ? (year & 3) || (!(year % 25) && year & 15) ? 28 : 29 : 30 + (month + (month >> 3) & 1);
}
- // Massive function to build the month
- function setMonthly(m, y){
- $('#' + uniqueId).data('setMonth', m).data('setYear', y);
+ // Build the month
+ function setMonthly(month, year) {
+ $(parent).data("setMonth", month).data("setYear", year);
// Get number of days
- var dayQty = daysInMonth(m, y),
+ var index = 0,
+ dayQty = daysInMonth(month, year),
// Get day of the week the first day is
- mZeroed = m -1,
- firstDay = new Date(y, mZeroed, 1, 0, 0, 0, 0).getDay();
+ mZeroed = month - 1,
+ firstDay = new Date(year, mZeroed, 1, 0, 0, 0, 0).getDay(),
+ settingCurrentMonth = month === currentMonth && year === currentYear;
// Remove old days
- $('#' + uniqueId + ' .monthly-day, #' + uniqueId + ' .monthly-day-blank').remove();
- $('#'+uniqueId+' .monthly-event-list').empty();
- $('#'+uniqueId+' .monthly-day-wrap').empty();
+ $(parent + " .monthly-day, " + parent + " .monthly-day-blank").remove();
+ $(parent + " .monthly-event-list, " + parent + " .monthly-day-wrap").empty();
// Print out the days
- if (options.mode == 'event') {
- for(var i = 0; i < dayQty; i++) {
-
- var day = i + 1; // Fix 0 indexed days
- var dayNamenum = new Date(y, mZeroed, day, 0, 0, 0, 0).getDay()
-
- $('#' + uniqueId + ' .monthly-day-wrap').append('
');
- }
- } else {
- for(var i = 0; i < dayQty; i++) {
- // Fix 0 indexed days
- var day = i + 1;
-
- // Check if it's a day in the past
- if(((day < currentDay && m === currentMonth) || y < currentYear || (m < currentMonth && y == currentYear)) && options.stylePast == true){
- $('#' + uniqueId + ' .monthly-day-wrap').append('
';
- }
- $('#'+uniqueId+' .monthly-list-item[data-number="'+startDay+'"]').addClass('item-has-event').append(''+eventTitle+' '+timeHtml+'');
-
-
- // If event is multi day & within month
- } else if (startMonth == setMonth && startYear == setYear && endMonth == setMonth && endYear == setYear){
- for(var i = parseInt(startDay); i <= parseInt(endDay); i++) {
- // If first day, add title
- if (i == parseInt(startDay)) {
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('
'+eventTitle+'
');
- } else {
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('');
- }
- multidaylist();
- }
-
- // If event is multi day, starts in prev month, and ends in current month
- } else if ((endMonth == setMonth && endYear == setYear) && ((startMonth < setMonth && startYear == setYear) || (startYear < setYear))) {
- for(var i = 0; i <= parseInt(endDay); i++) {
- // If first day, add title
- if (i==1){
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('
'+eventTitle+'
');
- } else {
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('');
- }
- multidaylist();
- }
-
- // If event is multi day, starts in this month, but ends in next
- } else if ((startMonth == setMonth && startYear == setYear) && ((endMonth > setMonth && endYear == setYear) || (endYear > setYear))){
- for(var i = parseInt(startDay); i <= dayQty; i++) {
- // If first day, add title
- if (i == parseInt(startDay)) {
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('
'+eventTitle+'
');
- } else {
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('');
- }
- multidaylist();
- }
-
- // If event is multi day, starts in a prev month, ends in a future month
- } else if (((startMonth < setMonth && startYear == setYear) || (startYear < setYear)) && ((endMonth > setMonth && endYear == setYear) || (endYear > setYear))){
- for(var i = 0; i <= dayQty; i++) {
- // If first day, add title
- if (i == 1){
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('
'+eventTitle+'
');
- } else {
- $('#'+uniqueId+' *[data-number="'+i+'"] .monthly-indicator-wrap').append('');
- }
- multidaylist();
- }
+ function addEvents(month, year) {
+ if(options.events) {
+ // Prefer local events if provided
+ addEventsFromString(options.events, month, year);
+ } else {
+ var remoteUrl = options.dataType === "xml" ? options.xmlUrl : options.jsonUrl;
+ if(remoteUrl) {
+ // Replace variables for month and year to load from dynamic sources
+ var url = String(remoteUrl).replace("{month}", month).replace("{year}", year);
+ $.get(url, {now: $.now()}, function(data) {
+ addEventsFromString(data, month, year);
+ }, options.dataType).fail(function() {
+ console.error("Monthly.js failed to import " + remoteUrl + ". Please check for the correct path and " + options.dataType + " syntax.");
+ });
+ }
+ }
+ }
- }
- };
-
- var eventsResource = (options.dataType == 'xml' ? options.xmlUrl : options.jsonUrl);
-
- $.get(''+eventsResource+'', {now: jQuery.now()}, function(d){
- if (options.dataType == 'xml') {
- $(d).find('event').each(function(index, event) {
- addEvents(event);
- });
- } else if (options.dataType == 'json') {
- $.each(d.monthly, function(index, event) {
- addEvents(event);
- });
- }
- }, options.dataType).fail(function() {
- console.error('Monthly.js failed to import '+eventsResource+'. Please check for the correct path & '+options.dataType+' syntax.');
+ function addEventsFromString(events, setMonth, setYear) {
+ if (options.dataType === "xml") {
+ $(events).find("event").each(function(index, event) {
+ addEvent(event, setMonth, setYear);
});
+ } else if (options.dataType === "json") {
+ $.each(events.monthly, function(index, event) {
+ addEvent(event, setMonth, setYear);
+ });
+ }
+ }
+ function attr(name, value) {
+ var parseValue = String(value);
+ var newValue = "";
+ for(var index = 0; index < parseValue.length; index++) {
+ switch(parseValue[index]) {
+ case "'": newValue += "'"; break;
+ case "\"": newValue += """; break;
+ case "<": newValue += "<"; break;
+ case ">": newValue += ">"; break;
+ default: newValue += parseValue[index];
+ }
}
- var divs = $("#"+uniqueId+" .m-d");
- for(var i = 0; i < divs.length; i+=7) {
- divs.slice(i, i+7).wrapAll("");
+ return " " + name + "=\"" + newValue + "\"";
+ }
+
+ function _appendDayNames(startOnMonday) {
+ var offset = startOnMonday ? 1 : 0,
+ dayName = "",
+ dayIndex = 0;
+ for(dayIndex = 0; dayIndex < 6; dayIndex++) {
+ dayName += "
" + dayNames[dayIndex + offset] + "
";
}
+ dayName += "
" + dayNames[startOnMonday ? 0 : 6] + "
";
+ $(parent).append('
' + dayName + '
');
}
- // Set the calendar the first time
- setMonthly(currentMonth, currentYear);
+ // Detect the user's preferred language
+ function defaultLocale() {
+ return navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language;
+ }
- // Function to go back to the month view
- function viewToggleButton(){
- if($('#'+uniqueId+' .monthly-event-list').is(":visible")) {
- $('#'+uniqueId+' .monthly-cal').remove();
- $('#'+uniqueId+' .monthly-header-title').prepend('☷ MONTH');
+ // Use the user's locale if possible to obtain a list of short month names, falling back on English
+ function defaultMonthNames() {
+ if(typeof Intl === "undefined") {
+ return ["Jan", "Feb", "Mar", "Apr", "May", "June", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+ }
+ var formatter = new Intl.DateTimeFormat(locale, {month: monthNameFormat});
+ var names = [];
+ for(var monthIndex = 0; monthIndex < 12; monthIndex++) {
+ var sampleDate = new Date(2017, monthIndex, 1, 0, 0, 0);
+ names[monthIndex] = formatter.format(sampleDate);
}
+ return names;
}
- // Advance months
- $(document.body).on('click', '#'+uniqueId+' .monthly-next', function (e) {
- var setMonth = $('#' + uniqueId).data('setMonth'),
- setYear = $('#' + uniqueId).data('setYear');
- if (setMonth == 12) {
- var newMonth = 1,
- newYear = setYear + 1;
- setMonthly(newMonth, newYear);
- } else {
- var newMonth = setMonth + 1,
- newYear = setYear;
- setMonthly(newMonth, newYear);
+ function formatDate(year, month, day) {
+ if(options.useIsoDateFormat) {
+ return new Date(year, month - 1, day, 0, 0, 0).toISOString().substring(0, 10);
+ }
+ if(typeof Intl === "undefined") {
+ return month + "/" + day + "/" + year;
+ }
+ return new Intl.DateTimeFormat(locale).format(new Date(year, month - 1, day, 0, 0, 0));
+ }
+
+ // Use the user's locale if possible to obtain a list of short weekday names, falling back on English
+ function defaultDayNames() {
+ if(typeof Intl === "undefined") {
+ return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+ }
+ var formatter = new Intl.DateTimeFormat(locale, {weekday: weekdayNameFormat}),
+ names = [],
+ dayIndex = 0,
+ sampleDate = null;
+ for(dayIndex = 0; dayIndex < 7; dayIndex++) {
+ // 2017 starts on a Sunday, so use it to capture the locale's weekday names
+ sampleDate = new Date(2017, 0, dayIndex + 1, 0, 0, 0);
+ names[dayIndex] = formatter.format(sampleDate);
+ }
+ return names;
+ }
+
+ function _prependBlankDays(count) {
+ var wrapperEl = $(parent + " .monthly-day-wrap"),
+ index = 0;
+ for(index = 0; index < count; index++) {
+ wrapperEl.prepend(markupBlankDay);
+ }
+ }
+
+ function _getEventDetail(event, nodeName) {
+ return options.dataType === "xml" ? $(event).find(nodeName).text() : event[nodeName];
+ }
+
+ // Returns a 12-hour format hour/minute with period. Opportunity for future localization.
+ function formatTime(value) {
+ var timeSplit = value.split(":");
+ var hour = parseInt(timeSplit[0], 10);
+ var period = "AM";
+ if(hour > 12) {
+ hour -= 12;
+ period = "PM";
+ } else if(hour === 0) {
+ hour = 12;
}
+ return hour + ":" + String(timeSplit[1]) + " " + period;
+ }
+
+ function setNextMonth() {
+ var setMonth = $(parent).data("setMonth"),
+ setYear = $(parent).data("setYear"),
+ newMonth = setMonth === 12 ? 1 : setMonth + 1,
+ newYear = setMonth === 12 ? setYear + 1 : setYear;
+ setMonthly(newMonth, newYear);
viewToggleButton();
- e.preventDefault();
+ }
+
+ function setPreviousMonth() {
+ var setMonth = $(parent).data("setMonth"),
+ setYear = $(parent).data("setYear"),
+ newMonth = setMonth === 1 ? 12 : setMonth - 1,
+ newYear = setMonth === 1 ? setYear - 1 : setYear;
+ setMonthly(newMonth, newYear);
+ viewToggleButton();
+ }
+
+ // Function to go back to the month view
+ function viewToggleButton() {
+ if($(parent + " .monthly-event-list").is(":visible")) {
+ $(parent + " .monthly-cal").remove();
+ $(parent + " .monthly-header-title").prepend('');
+ }
+ }
+
+ // Advance months
+ $(document.body).on("click", parent + " .monthly-next", function (event) {
+ setNextMonth();
+ event.preventDefault();
});
// Go back in months
- $(document.body).on('click', '#'+uniqueId+' .monthly-prev', function (e) {
- var setMonth = $('#' + uniqueId).data('setMonth'),
- setYear = $('#' + uniqueId).data('setYear');
- if (setMonth == 1) {
- var newMonth = 12,
- newYear = setYear - 1;
- setMonthly(newMonth, newYear);
- } else {
- var newMonth = setMonth - 1,
- newYear = setYear;
- setMonthly(newMonth, newYear);
- }
- viewToggleButton();
- e.preventDefault();
+ $(document.body).on("click", parent + " .monthly-prev", function (event) {
+ setPreviousMonth();
+ event.preventDefault();
});
// Reset Month
- $(document.body).on('click', '#'+uniqueId+' .monthly-reset', function (e) {
+ $(document.body).on("click", parent + " .monthly-reset", function (event) {
$(this).remove();
setMonthly(currentMonth, currentYear);
viewToggleButton();
- e.preventDefault();
- e.stopPropagation();
+ event.preventDefault();
+ event.stopPropagation();
});
// Back to month view
- $(document.body).on('click', '#'+uniqueId+' .monthly-cal', function (e) {
+ $(document.body).on("click", parent + " .monthly-cal", function (event) {
$(this).remove();
- $('#' + uniqueId+' .monthly-event-list').css('transform','scale(0)');
- setTimeout(function(){
- $('#' + uniqueId+' .monthly-event-list').hide();
- }, 250);
- e.preventDefault();
+ $(parent + " .monthly-event-list").css("transform", "scale(0)");
+ setTimeout(function() {
+ $(parent + " .monthly-event-list").hide();
+ }, 250);
+ event.preventDefault();
});
// Click A Day
- $(document.body).on('click', '#'+uniqueId+' a.monthly-day', function (e) {
+ $(document.body).on("click touchstart", parent + " .monthly-day", function (event) {
// If events, show events list
- if(options.mode == 'event' && options.eventList == true) {
- var whichDay = $(this).data('number');
- $('#' + uniqueId+' .monthly-event-list').show();
- $('#' + uniqueId+' .monthly-event-list').css('transform');
- $('#' + uniqueId+' .monthly-event-list').css('transform','scale(1)');
- $('#' + uniqueId+' .monthly-list-item[data-number="'+whichDay+'"]').show();
-
- var myElement = document.getElementById(uniqueId+'day'+whichDay);
- var topPos = myElement.offsetTop;
- $('#' + uniqueId+' .monthly-event-list').scrollTop(topPos);
+ var whichDay = $(this).data("number");
+ if(options.mode === "event" && options.eventList) {
+ var theList = $(parent + " .monthly-event-list"),
+ myElement = document.getElementById(uniqueId + "day" + whichDay),
+ topPos = myElement.offsetTop;
+ theList.show();
+ theList.css("transform");
+ theList.css("transform", "scale(1)");
+ $(parent + ' .monthly-list-item[data-number="' + whichDay + '"]').show();
+ theList.scrollTop(topPos);
viewToggleButton();
+ if(!options.linkCalendarToEventUrl) {
+ event.preventDefault();
+ }
// If picker, pick date
- } else if (options.mode == 'picker') {
- var whichDay = $(this).data('number'),
- setMonth = $('#' + uniqueId).data('setMonth'),
- setYear = $('#' + uniqueId).data('setYear');
-
+ } else if (options.mode === "picker") {
+ var setMonth = $(parent).data("setMonth"),
+ setYear = $(parent).data("setYear");
// Should days in the past be disabled?
- if($(this).hasClass('monthly-past-day') && options.disablePast == true) {
+ if($(this).hasClass("monthly-past-day") && options.disablePast) {
// If so, don't do anything.
- e.preventDefault();
+ event.preventDefault();
} else {
// Otherwise, select the date ...
- $(''+options.target+'').val(setMonth+'/'+whichDay+'/'+setYear);
+ $(String(options.target)).val(formatDate(setYear, setMonth, whichDay));
// ... and then hide the calendar if it started that way
- if(options.startHidden == true) {
- $('#'+uniqueId).hide();
+ if(options.startHidden) {
+ $(parent).hide();
}
}
+ event.preventDefault();
}
- e.preventDefault();
});
// Clicking an event within the list
- $(document.body).on('click', '#'+uniqueId+' .listed-event', function (e) {
- var href = $(this).attr('href');
+ $(document.body).on("click", parent + " .listed-event", function (event) {
+ var href = $(this).attr("href");
// If there isn't a link, don't go anywhere
if(!href) {
- e.preventDefault();
+ event.preventDefault();
}
});
- }
+ }
});
-})(jQuery);
+}(jQuery));