2018-06-15 12:42:20 -04:00
|
|
|
|
import { parseBBCodeTag } from "pretty-text/engines/discourse-markdown/bbcode-block";
|
2018-05-04 02:51:41 -04:00
|
|
|
|
|
2021-08-04 02:27:22 -04:00
|
|
|
|
const timezoneNames = moment.tz.names();
|
|
|
|
|
|
2022-01-10 02:02:36 -05:00
|
|
|
|
function addSingleLocalDate(buffer, state, config) {
|
|
|
|
|
let token = new state.Token("span_open", "span", 1);
|
2018-11-26 08:20:32 -05:00
|
|
|
|
token.attrs = [["data-date", state.md.utils.escapeHtml(config.date)]];
|
|
|
|
|
|
|
|
|
|
if (!config.date.match(/\d{4}-\d{2}-\d{2}/)) {
|
|
|
|
|
closeBuffer(buffer, state, moment.invalid().format());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.time && !config.time.match(/\d{2}:\d{2}(?::\d{2})?/)) {
|
|
|
|
|
closeBuffer(buffer, state, moment.invalid().format());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-08-27 05:19:30 -04:00
|
|
|
|
|
2018-10-16 06:49:43 -04:00
|
|
|
|
let dateTime = config.date;
|
2018-10-11 17:41:57 -04:00
|
|
|
|
if (config.time) {
|
2018-10-11 18:26:35 -04:00
|
|
|
|
token.attrs.push(["data-time", state.md.utils.escapeHtml(config.time)]);
|
2018-10-16 06:49:43 -04:00
|
|
|
|
dateTime = `${dateTime} ${config.time}`;
|
2018-10-11 17:41:57 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 08:20:32 -05:00
|
|
|
|
if (!moment(dateTime).isValid()) {
|
|
|
|
|
closeBuffer(buffer, state, moment.invalid().format());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token.attrs.push(["class", "discourse-local-date"]);
|
|
|
|
|
|
2018-11-22 11:19:24 -05:00
|
|
|
|
if (config.format) {
|
|
|
|
|
token.attrs.push(["data-format", state.md.utils.escapeHtml(config.format)]);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 12:39:20 -04:00
|
|
|
|
if (config.countdown) {
|
|
|
|
|
token.attrs.push([
|
|
|
|
|
"data-countdown",
|
|
|
|
|
state.md.utils.escapeHtml(config.countdown),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 11:19:24 -05:00
|
|
|
|
if (config.calendar) {
|
|
|
|
|
token.attrs.push([
|
|
|
|
|
"data-calendar",
|
|
|
|
|
state.md.utils.escapeHtml(config.calendar),
|
|
|
|
|
]);
|
|
|
|
|
}
|
2022-01-10 02:02:36 -05:00
|
|
|
|
if (config.range) {
|
2022-10-06 21:39:41 -04:00
|
|
|
|
token.attrs.push(["data-range", config.range]);
|
2022-01-10 02:02:36 -05:00
|
|
|
|
}
|
2018-11-22 11:19:24 -05:00
|
|
|
|
|
2018-11-26 08:20:32 -05:00
|
|
|
|
if (
|
|
|
|
|
config.displayedTimezone &&
|
2021-08-04 02:27:22 -04:00
|
|
|
|
timezoneNames.includes(config.displayedTimezone)
|
2018-11-26 08:20:32 -05:00
|
|
|
|
) {
|
2018-11-22 11:19:24 -05:00
|
|
|
|
token.attrs.push([
|
2018-11-26 08:20:32 -05:00
|
|
|
|
"data-displayed-timezone",
|
|
|
|
|
state.md.utils.escapeHtml(config.displayedTimezone),
|
2018-11-22 11:19:24 -05:00
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.timezones) {
|
2018-11-26 08:20:32 -05:00
|
|
|
|
const timezones = config.timezones.split("|").filter((timezone) => {
|
2021-08-04 02:27:22 -04:00
|
|
|
|
return timezoneNames.includes(timezone);
|
2018-11-26 08:20:32 -05:00
|
|
|
|
});
|
|
|
|
|
|
2018-11-22 11:19:24 -05:00
|
|
|
|
token.attrs.push([
|
|
|
|
|
"data-timezones",
|
2018-11-26 08:20:32 -05:00
|
|
|
|
state.md.utils.escapeHtml(timezones.join("|")),
|
2018-11-22 11:19:24 -05:00
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 02:27:22 -04:00
|
|
|
|
if (config.timezone && timezoneNames.includes(config.timezone)) {
|
2018-09-16 22:26:48 -04:00
|
|
|
|
token.attrs.push([
|
2018-10-15 19:34:55 -04:00
|
|
|
|
"data-timezone",
|
|
|
|
|
state.md.utils.escapeHtml(config.timezone),
|
2018-09-16 22:26:48 -04:00
|
|
|
|
]);
|
2018-10-16 06:49:43 -04:00
|
|
|
|
dateTime = moment.tz(dateTime, config.timezone);
|
2018-10-09 10:45:32 -04:00
|
|
|
|
} else {
|
2018-10-16 06:49:43 -04:00
|
|
|
|
dateTime = moment.utc(dateTime);
|
2018-09-12 09:54:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 14:45:32 -04:00
|
|
|
|
if (config.recurring) {
|
2018-08-27 05:19:30 -04:00
|
|
|
|
token.attrs.push([
|
|
|
|
|
"data-recurring",
|
|
|
|
|
state.md.utils.escapeHtml(config.recurring),
|
|
|
|
|
]);
|
2018-05-04 14:45:32 -04:00
|
|
|
|
}
|
2018-11-22 11:19:24 -05:00
|
|
|
|
|
2018-05-04 02:51:41 -04:00
|
|
|
|
buffer.push(token);
|
|
|
|
|
|
2019-01-16 06:53:41 -05:00
|
|
|
|
const formattedDateTime = dateTime
|
|
|
|
|
.tz("Etc/UTC")
|
|
|
|
|
.format(
|
|
|
|
|
state.md.options.discourse.datesEmailFormat || moment.defaultFormat
|
|
|
|
|
);
|
|
|
|
|
token.attrs.push(["data-email-preview", `${formattedDateTime} UTC`]);
|
2018-05-18 09:35:37 -04:00
|
|
|
|
|
2018-11-26 08:20:32 -05:00
|
|
|
|
closeBuffer(buffer, state, dateTime.utc().format(config.format));
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-10 02:02:36 -05:00
|
|
|
|
function defaultDateConfig() {
|
|
|
|
|
return {
|
|
|
|
|
date: null,
|
|
|
|
|
time: null,
|
|
|
|
|
timezone: null,
|
|
|
|
|
format: null,
|
|
|
|
|
timezones: null,
|
|
|
|
|
displayedTimezone: null,
|
|
|
|
|
countdown: null,
|
|
|
|
|
range: false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseTagAttributes(tag) {
|
|
|
|
|
const matchString = tag.replace(/‘|’|„|“|«|»|”/g, '"');
|
|
|
|
|
|
|
|
|
|
return parseBBCodeTag(
|
|
|
|
|
"[date date" + matchString + "]",
|
|
|
|
|
0,
|
|
|
|
|
matchString.length + 12
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addLocalDate(buffer, matches, state) {
|
|
|
|
|
let config = defaultDateConfig();
|
|
|
|
|
|
|
|
|
|
const parsed = parseTagAttributes(matches[1]);
|
|
|
|
|
|
|
|
|
|
config.date = parsed.attrs.date;
|
|
|
|
|
config.format = parsed.attrs.format;
|
|
|
|
|
config.calendar = parsed.attrs.calendar;
|
|
|
|
|
config.time = parsed.attrs.time;
|
|
|
|
|
config.timezone = (parsed.attrs.timezone || "").trim();
|
|
|
|
|
config.recurring = parsed.attrs.recurring;
|
|
|
|
|
config.timezones = parsed.attrs.timezones;
|
|
|
|
|
config.displayedTimezone = parsed.attrs.displayedTimezone;
|
|
|
|
|
config.countdown = parsed.attrs.countdown;
|
|
|
|
|
addSingleLocalDate(buffer, state, config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addLocalRange(buffer, matches, state) {
|
|
|
|
|
let config = defaultDateConfig();
|
|
|
|
|
let date, time;
|
|
|
|
|
const parsed = parseTagAttributes(matches[1]);
|
|
|
|
|
|
|
|
|
|
config.format = parsed.attrs.format;
|
|
|
|
|
config.calendar = parsed.attrs.calendar;
|
|
|
|
|
config.timezone = (parsed.attrs.timezone || "").trim();
|
|
|
|
|
config.recurring = parsed.attrs.recurring;
|
|
|
|
|
config.timezones = parsed.attrs.timezones;
|
|
|
|
|
config.displayedTimezone = parsed.attrs.displayedTimezone;
|
|
|
|
|
config.countdown = parsed.attrs.countdown;
|
|
|
|
|
|
|
|
|
|
if (parsed.attrs.from) {
|
|
|
|
|
[date, time] = parsed.attrs.from.split("T");
|
|
|
|
|
config.date = date;
|
|
|
|
|
config.time = time;
|
2022-10-06 21:39:41 -04:00
|
|
|
|
config.range = "from";
|
2022-01-10 02:02:36 -05:00
|
|
|
|
addSingleLocalDate(buffer, state, config);
|
|
|
|
|
}
|
|
|
|
|
if (config.range) {
|
|
|
|
|
closeBuffer(buffer, state, "→");
|
|
|
|
|
}
|
|
|
|
|
if (parsed.attrs.to) {
|
|
|
|
|
[date, time] = parsed.attrs.to.split("T");
|
|
|
|
|
config.date = date;
|
|
|
|
|
config.time = time;
|
2022-10-06 21:39:41 -04:00
|
|
|
|
config.range = "to";
|
2022-01-10 02:02:36 -05:00
|
|
|
|
addSingleLocalDate(buffer, state, config);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 08:20:32 -05:00
|
|
|
|
function closeBuffer(buffer, state, text) {
|
|
|
|
|
let token;
|
|
|
|
|
|
2018-06-15 12:42:20 -04:00
|
|
|
|
token = new state.Token("text", "", 0);
|
2018-11-26 08:20:32 -05:00
|
|
|
|
token.content = text;
|
2018-05-04 02:51:41 -04:00
|
|
|
|
buffer.push(token);
|
|
|
|
|
|
2018-06-15 12:42:20 -04:00
|
|
|
|
token = new state.Token("span_close", "span", -1);
|
2018-11-22 11:19:24 -05:00
|
|
|
|
|
2018-05-04 02:51:41 -04:00
|
|
|
|
buffer.push(token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function setup(helper) {
|
2021-09-22 10:49:45 -04:00
|
|
|
|
helper.allowList([
|
|
|
|
|
"span.discourse-local-date",
|
|
|
|
|
"span[aria-label]",
|
|
|
|
|
"span[data-date]",
|
|
|
|
|
"span[data-time]",
|
|
|
|
|
"span[data-format]",
|
|
|
|
|
"span[data-countdown]",
|
|
|
|
|
"span[data-calendar]",
|
|
|
|
|
"span[data-displayed-timezone]",
|
|
|
|
|
"span[data-timezone]",
|
|
|
|
|
"span[data-timezones]",
|
|
|
|
|
"span[data-recurring]",
|
|
|
|
|
"span[data-email-preview]",
|
|
|
|
|
]);
|
2018-05-04 02:51:41 -04:00
|
|
|
|
|
|
|
|
|
helper.registerOptions((opts, siteSettings) => {
|
2019-01-16 06:53:41 -05:00
|
|
|
|
opts.datesEmailFormat = siteSettings.discourse_local_dates_email_format;
|
|
|
|
|
|
2022-07-06 04:37:54 -04:00
|
|
|
|
opts.features["discourse-local-dates"] =
|
|
|
|
|
!!siteSettings.discourse_local_dates_enabled;
|
2018-05-04 02:51:41 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
helper.registerPlugin((md) => {
|
|
|
|
|
const rule = {
|
2019-02-18 16:39:45 -05:00
|
|
|
|
matcher: /\[date(=.+?)\]/,
|
2018-05-04 02:51:41 -04:00
|
|
|
|
onMatch: addLocalDate,
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-15 12:42:20 -04:00
|
|
|
|
md.core.textPostProcess.ruler.push("discourse-local-dates", rule);
|
2018-05-04 02:51:41 -04:00
|
|
|
|
});
|
2022-01-10 02:02:36 -05:00
|
|
|
|
|
|
|
|
|
helper.registerPlugin((md) => {
|
|
|
|
|
const rule = {
|
|
|
|
|
matcher: /\[date-range(.+?)\]/,
|
|
|
|
|
onMatch: addLocalRange,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
md.core.textPostProcess.ruler.push("discourse-local-dates", rule);
|
|
|
|
|
});
|
2018-05-04 02:51:41 -04:00
|
|
|
|
}
|