mirror of
https://github.com/discourse/discourse.git
synced 2025-02-07 11:58:27 +00:00
03deda2147
* Add missing icons to set * Revert FA5 revert This reverts commit 42572ff * use new SVG syntax in locales * Noscript page changes (remove login button, center "powered by" footer text) * Cast wider net for SVG icons in settings - include any _icon setting for SVG registry (offers better support for plugin settings) - let themes store multiple pipe-delimited icons in a setting - also replaces broken onebox image icon with SVG reference in cooked post processor * interpolate icons in locales * Fix composer whisper icon alignment * Add support for stacked icons * SECURITY: enforce hostname to match discourse hostname This ensures that the hostname rails uses for various helpers always matches the Discourse hostname * load SVG sprite with pre-initializers * FIX: enable caching on SVG sprites * PERF: use JSONP for SVG sprites so they are served from CDN This avoids needing to deal with CORS for loading of the SVG Note, added the svg- prefix to the filename so we can quickly tell in dev tools what the file is * Add missing SVG sprite JSONP script to CSP * Upgrade to FA 5.5.0 * Add support for all FA4.7 icons - adds complete frontend and backend for renamed FA4.7 icons - improves performance of SvgSprite.bundle and SvgSprite.all_icons * Fix group avatar flair preview - adds an endpoint at /svg-sprites/search/:keyword - adds frontend ajax call that pulls icon in avatar flair preview even when it is not in subset * Remove FA 4.7 font files
286 lines
8.4 KiB
JavaScript
286 lines
8.4 KiB
JavaScript
(function($) {
|
|
$.fn.applyLocalDates = function(repeat) {
|
|
const processElement = ($element, options = {}) => {
|
|
if (this.timeout) clearTimeout(this.timeout);
|
|
|
|
repeat = repeat || true;
|
|
const utc = moment().utc();
|
|
const dateTime = options.time
|
|
? `${options.date} ${options.time}`
|
|
: options.date;
|
|
let utcDateTime;
|
|
|
|
let displayedTimezone;
|
|
if (options.time) {
|
|
displayedTimezone = options.displayedTimezone || moment.tz.guess();
|
|
} else {
|
|
displayedTimezone =
|
|
options.displayedTimezone || options.timezone || moment.tz.guess();
|
|
}
|
|
|
|
// if timezone given we convert date and time from given zone to Etc/UTC
|
|
if (options.timezone) {
|
|
utcDateTime = _applyZoneToDateTime(dateTime, options.timezone);
|
|
} else {
|
|
utcDateTime = moment.utc(dateTime);
|
|
}
|
|
|
|
if (utcDateTime < utc) {
|
|
// if event is in the past we want to bump it no next occurrence when
|
|
// recurring is set
|
|
if (options.recurring) {
|
|
utcDateTime = _applyRecurrence(utcDateTime, options.recurring);
|
|
} else {
|
|
$element.addClass("past");
|
|
}
|
|
}
|
|
|
|
// once we have the correct UTC date we want
|
|
// we adjust it to watching user timezone
|
|
const adjustedDateTime = utcDateTime.tz(displayedTimezone);
|
|
|
|
const previews = _generatePreviews(
|
|
adjustedDateTime.clone(),
|
|
displayedTimezone,
|
|
options
|
|
);
|
|
const textPreview = _generateTextPreview(previews);
|
|
const htmlPreview = _generateHtmlPreview(previews);
|
|
|
|
const formatedDateTime = _applyFormatting(
|
|
adjustedDateTime,
|
|
displayedTimezone,
|
|
options
|
|
);
|
|
|
|
const $dateTemplate = `
|
|
<span>
|
|
<svg class="fa d-icon d-icon-globe-americas svg-icon" xmlns="http://www.w3.org/2000/svg">
|
|
<use xlink:href="#globe-americas"></use>
|
|
</svg>
|
|
<span class="relative-time"></span>
|
|
</span>
|
|
`;
|
|
|
|
$element
|
|
.html($dateTemplate)
|
|
.attr("title", textPreview)
|
|
.attr("data-html-tooltip", `<div class="previews">${htmlPreview}</div>`)
|
|
.addClass("cooked-date")
|
|
.find(".relative-time")
|
|
.text(formatedDateTime);
|
|
|
|
if (repeat) {
|
|
this.timeout = setTimeout(
|
|
() => processElement($element, options),
|
|
10000
|
|
);
|
|
}
|
|
};
|
|
|
|
const _formatTimezone = timezone =>
|
|
timezone
|
|
.replace("_", " ")
|
|
.replace("Etc/", "")
|
|
.split("/");
|
|
|
|
const _applyZoneToDateTime = (dateTime, timezone) => {
|
|
return moment.tz(dateTime, timezone).utc();
|
|
};
|
|
|
|
const _calendarFormats = time => {
|
|
const _translate = key => {
|
|
const translated = I18n.t(
|
|
`discourse_local_dates.relative_dates.${key}`,
|
|
{
|
|
time: "LT"
|
|
}
|
|
);
|
|
|
|
if (time) {
|
|
return translated
|
|
.split("LT")
|
|
.map(w => `[${w}]`)
|
|
.join("LT");
|
|
} else {
|
|
return `[${translated.replace(" LT", "")}]`;
|
|
}
|
|
};
|
|
|
|
return {
|
|
sameDay: _translate("today"),
|
|
nextDay: _translate("tomorrow"),
|
|
lastDay: _translate("yesterday"),
|
|
sameElse: "L"
|
|
};
|
|
};
|
|
|
|
const _applyFormatting = (dateTime, displayedTimezone, options) => {
|
|
const sameTimezone = displayedTimezone === moment.tz.guess();
|
|
const inCalendarRange = dateTime.isBetween(
|
|
moment().subtract(2, "days"),
|
|
moment().add(2, "days")
|
|
);
|
|
|
|
if (options.calendar && inCalendarRange) {
|
|
if (sameTimezone) {
|
|
if (options.time) {
|
|
dateTime = dateTime.calendar(null, _calendarFormats(options.time));
|
|
} else {
|
|
dateTime = dateTime.calendar(null, _calendarFormats(null));
|
|
}
|
|
} else {
|
|
dateTime = dateTime.format(options.format);
|
|
dateTime = dateTime.replace("TZ", "");
|
|
dateTime = `${dateTime} (${_formatTimezone(displayedTimezone).join(
|
|
": "
|
|
)})`;
|
|
}
|
|
} else {
|
|
if (options.time) {
|
|
dateTime = dateTime.format(options.format);
|
|
|
|
if (options.displayedTimezone && !sameTimezone) {
|
|
dateTime = dateTime.replace("TZ", "");
|
|
dateTime = `${dateTime} (${_formatTimezone(displayedTimezone).join(
|
|
": "
|
|
)})`;
|
|
} else {
|
|
dateTime = dateTime.replace(
|
|
"TZ",
|
|
_formatTimezone(displayedTimezone).join(": ")
|
|
);
|
|
}
|
|
} else {
|
|
dateTime = dateTime.format(options.format);
|
|
|
|
if (!sameTimezone) {
|
|
dateTime = dateTime.replace("TZ", "");
|
|
dateTime = `${dateTime} (${_formatTimezone(displayedTimezone).join(
|
|
": "
|
|
)})`;
|
|
} else {
|
|
dateTime = dateTime.replace(
|
|
"TZ",
|
|
_formatTimezone(displayedTimezone).join(": ")
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dateTime;
|
|
};
|
|
|
|
const _applyRecurrence = (dateTime, recurring) => {
|
|
const parts = recurring.split(".");
|
|
const count = parseInt(parts[0], 10);
|
|
const type = parts[1];
|
|
const diff = moment().diff(dateTime, type);
|
|
const add = Math.ceil(diff + count);
|
|
|
|
return dateTime.add(add, type);
|
|
};
|
|
|
|
const createDateTimeRange = (dateTime, timezone) => {
|
|
const startRange = dateTime.tz(timezone).format("LLL");
|
|
const separator = "→";
|
|
const endRange = dateTime
|
|
.add(24, "hours")
|
|
.tz(timezone)
|
|
.format("LLL");
|
|
|
|
return `${startRange} ${separator} ${endRange}`;
|
|
};
|
|
|
|
const _generatePreviews = (dateTime, displayedTimezone, options) => {
|
|
const previewedTimezones = [];
|
|
const watchingUserTimezone = moment.tz.guess();
|
|
|
|
if (displayedTimezone !== watchingUserTimezone) {
|
|
previewedTimezones.push({
|
|
timezone: watchingUserTimezone,
|
|
current: true,
|
|
dateTime: options.time
|
|
? dateTime.tz(watchingUserTimezone).format("LLL")
|
|
: createDateTimeRange(dateTime, watchingUserTimezone)
|
|
});
|
|
}
|
|
|
|
options.timezones
|
|
.filter(x => x !== watchingUserTimezone)
|
|
.forEach(timezone => {
|
|
previewedTimezones.push({
|
|
timezone,
|
|
dateTime: options.time
|
|
? dateTime.tz(timezone).format("LLL")
|
|
: createDateTimeRange(dateTime, timezone)
|
|
});
|
|
});
|
|
|
|
return previewedTimezones;
|
|
};
|
|
|
|
const _generateTextPreview = previews => {
|
|
return previews
|
|
.map(preview => {
|
|
const timezoneParts = _formatTimezone(preview.timezone);
|
|
|
|
if (preview.dateTime.match(/TZ/)) {
|
|
return preview.dateTime.replace(/TZ/, timezoneParts.join(": "));
|
|
} else {
|
|
let output = timezoneParts[0];
|
|
if (timezoneParts[1]) output += ` (${timezoneParts[1]})`;
|
|
return (output += ` ${preview.dateTime}`);
|
|
}
|
|
})
|
|
.join(", ");
|
|
};
|
|
|
|
const _generateHtmlPreview = previews => {
|
|
const $htmlTooltip = $("<div></div>");
|
|
|
|
const $previewTemplate = $(`
|
|
<div class='preview'>
|
|
<span class='timezone'></span>
|
|
<span class='date-time'></span>
|
|
</div>
|
|
`);
|
|
|
|
previews.forEach(preview => {
|
|
const $template = $previewTemplate.clone();
|
|
|
|
if (preview.current) $template.addClass("current");
|
|
|
|
$template
|
|
.find(".timezone")
|
|
.text(_formatTimezone(preview.timezone).join(": "));
|
|
$template.find(".date-time").text(preview.dateTime);
|
|
$htmlTooltip.append($template);
|
|
});
|
|
|
|
return $htmlTooltip.html();
|
|
};
|
|
|
|
return this.each(function() {
|
|
const $element = $(this);
|
|
|
|
const options = {};
|
|
options.time = $element.attr("data-time");
|
|
options.date = $element.attr("data-date");
|
|
options.recurring = $element.attr("data-recurring");
|
|
options.timezones = (
|
|
$element.attr("data-timezones") ||
|
|
Discourse.SiteSettings.discourse_local_dates_default_timezones ||
|
|
"Etc/UTC"
|
|
).split("|");
|
|
options.timezone = $element.attr("data-timezone");
|
|
options.calendar = ($element.attr("data-calendar") || "on") === "on";
|
|
options.displayedTimezone = $element.attr("data-displayed-timezone");
|
|
options.format =
|
|
$element.attr("data-format") || (options.time ? "LLL" : "LL");
|
|
|
|
processElement($element, options);
|
|
});
|
|
};
|
|
})(jQuery);
|