discourse-calendar/assets/javascripts/initializers/discourse-calendar.js.es6

314 lines
8.2 KiB
JavaScript

import { escapeExpression } from "discourse/lib/utilities";
import loadScript from "discourse/lib/load-script";
import { withPluginApi } from "discourse/lib/plugin-api";
import { ajax } from "discourse/lib/ajax";
import { showPopover, hidePopover } from "discourse/lib/d-popover";
// https://stackoverflow.com/a/16348977
/* eslint-disable */
// prettier-ignore
function stringToHexColor(str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
var hex = "#";
for (var i = 0; i < 3; i++) {
var value = (hash >> (i * 8)) & 0xff;
hex += ("00" + value.toString(16)).substr(-2);
}
return hex;
}
function initializeDiscourseCalendar(api) {
let _topicController;
api.decorateCooked(attachCalendar, {
onlyStream: true,
id: "discourse-calendar"
});
api.registerCustomPostMessageCallback("calendar_change", topicController => {
const stream = topicController.get("model.postStream");
const post = stream.findLoadedPost(stream.get("firstPostId"));
const $op = $(".topic-post article#post_1");
const $calendar = $op.find(".calendar").first();
if (post && $calendar.length > 0) {
ajax(`/posts/${post.id}.json`).then(post => {
loadScript(
"/plugins/discourse-calendar/javascripts/fullcalendar-with-moment-timezone.min.js"
).then(() => render($calendar, post));
});
}
});
function render($calendar, post) {
$calendar = $calendar.empty();
const calendar = _buildCalendar($calendar);
const isStatic = $calendar.attr("data-calendar-type") === "static";
if (isStatic) {
calendar.render();
_setStaticCalendarEvents(calendar, $calendar, post);
} else {
_setDynamicCalendarEvents(calendar, post);
calendar.render();
_setDynamicCalendarOptions(calendar, $calendar);
}
}
function attachCalendar($elem, helper) {
const $calendar = $(".calendar", $elem);
if ($calendar.length === 0) {
return;
}
loadScript(
"/plugins/discourse-calendar/javascripts/fullcalendar-with-moment-timezone.min.js"
).then(() => render($calendar, helper.getModel()));
}
function _buildCalendar($calendar) {
const defaultView = escapeExpression(
$calendar.attr("data-calendar-default-view") || "month"
);
return new window.FullCalendar.Calendar($calendar[0], {
timeZone: moment.tz.guess(),
timeZoneImpl: "moment-timezone",
nextDayThreshold: "23:59:59",
displayEventEnd: true,
height: 650,
firstDay: 1,
defaultView,
views: {
listNextYear: {
type: "list",
duration: { days: 365 },
buttonText: "list",
listDayFormat: {
month: "long",
year: "numeric",
day: "numeric",
weekday: "long"
}
}
},
header: {
left: "prev,next today",
center: "title",
right: "month,basicWeek,listNextYear"
}
});
}
function _convertHtmlToDate(html) {
const date = html.attr("data-date");
if (!date) {
return null;
}
const time = html.attr("data-time");
const timezone = html.attr("data-timezone");
let dateTime = date;
if (time) {
dateTime = `${dateTime} ${time}`;
}
return {
weeklyRecurring: html.attr("data-recurring") === "1.weeks",
dateTime: moment.tz(dateTime, timezone || "Etc/UTC")
};
}
function _buildEventObject(from, to) {
const hasTimeSpecified = d => {
return d.hours() !== 0 || d.minutes() !== 0 || d.seconds() !== 0;
};
let event = {
start: from.dateTime.toDate(),
allDay: true
};
if (from && !to) {
if (hasTimeSpecified(from.dateTime)) {
event.allDay = false;
}
if (from.weeklyRecurring) {
event.startTime = {
hours: from.dateTime.hours(),
minutes: from.dateTime.minutes(),
seconds: from.dateTime.seconds()
};
event.daysOfWeek = [from.dateTime.isoWeekday()];
}
}
if (from && to) {
if (hasTimeSpecified(from.dateTime) && hasTimeSpecified(to.dateTime)) {
event.end = to.dateTime.toDate();
event.allDay = false;
} else {
event.end = to.dateTime.add(1, "days").toDate();
}
}
return event;
}
function _setStaticCalendarEvents(calendar, $calendar, post) {
$(`<div>${post.cooked}</div>`)
.find('.calendar[data-calendar-type="static"] p')
.html()
.trim()
.split("<br>")
.forEach(line => {
const html = $.parseHTML(line);
const htmlDates = html.filter(h =>
$(h).hasClass("discourse-local-date")
);
const from = _convertHtmlToDate($(htmlDates[0]));
const to = _convertHtmlToDate($(htmlDates[1]));
let event = _buildEventObject(from, to);
event.title = html[0].textContent.trim();
calendar.addEvent(event);
});
}
function _setDynamicCalendarOptions(calendar, $calendar) {
const skipWeekends = $calendar.attr("data-weekends") === "false";
const hiddenDays = $calendar.attr("data-hidden-days");
if (skipWeekends) {
calendar.setOption("weekends", false);
}
if (hiddenDays) {
calendar.setOption(
"hiddenDays",
hiddenDays.split(",").map(d => parseInt(d))
);
}
calendar.setOption("eventClick", ({ event }) => {
const { postNumber } = event.extendedProps;
if (!postNumber) return;
_topicController =
_topicController || api.container.lookup("controller:topic");
_topicController.send("jumpToPost", postNumber);
});
calendar.setOption("eventMouseEnter", ({ event, jsEvent }) => {
const { htmlContent } = event.extendedProps;
if (!htmlContent) return;
showPopover(jsEvent, { htmlContent });
});
calendar.setOption("eventMouseLeave", ({ jsEvent }) => {
hidePopover(jsEvent);
});
calendar.setOption("eventRender", data => {
const emojiImage = data.event.extendedProps.emojiImage;
if (emojiImage) {
const element = data.el.querySelector(".fc-title");
$(element).prepend(emojiImage);
}
});
}
function _buildEvent(detail) {
const event = _buildEventObject(
detail.from
? {
dateTime: moment(detail.from),
weeklyRecurring: detail.recurring === "1.weeks"
}
: null,
detail.to
? {
dateTime: moment(detail.to),
weeklyRecurring: detail.recurring === "1.weeks"
}
: null
);
event.extendedProps = {};
if (detail.post_number) {
event.extendedProps.postNumber = detail.post_number;
} else {
event.classNames = ["holiday"];
}
return event;
}
function _addStandaloneEvent(calendar, post, detail) {
const event = _buildEvent(detail);
const holidayCalendarTopicId = parseInt(
Discourse.SiteSettings.holiday_calendar_topic_id,
10
);
const excerpt = detail.message.split("\n").filter(e => e);
if (
excerpt.length &&
post.topic_id &&
holidayCalendarTopicId !== post.topic_id
) {
event.title = excerpt[0];
} else {
event.title = detail.username;
event.backgroundColor = stringToHexColor(detail.username);
}
event.extendedProps.htmlContent = detail.message;
calendar.addEvent(event);
}
function _addGroupedEvent(calendar, post, detail) {
const event = _buildEvent(detail);
event.classNames = ["grouped-event"];
event.title = detail.name;
event.extendedProps.emojiImage = detail.emoji;
event.extendedProps.htmlContent = detail.usernames.join(", ");
calendar.addEvent(event);
}
function _setDynamicCalendarEvents(calendar, post) {
(post.calendar_details || []).forEach(detail => {
switch (detail.type) {
case "grouped":
_addGroupedEvent(calendar, post, detail);
break;
case "standalone":
_addStandaloneEvent(calendar, post, detail);
break;
}
});
}
}
export default {
name: "discourse-calendar",
initialize(container) {
const siteSettings = container.lookup("site-settings:main");
if (siteSettings.calendar_enabled) {
withPluginApi("0.8.22", initializeDiscourseCalendar);
}
}
};