DEV: local-dates refactoring (#6692)

This commit is contained in:
Joffrey JAFFEUX 2018-11-28 16:19:25 +01:00 committed by GitHub
parent 654b80e472
commit eb1607bd98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 246 additions and 256 deletions

View File

@ -1,294 +1,284 @@
(function($) {
$.fn.applyLocalDates = function(repeat) {
const processElement = ($element, options = {}) => {
if (this.timeout) clearTimeout(this.timeout);
const DATE_TEMPLATE = `
<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>
`;
repeat = repeat || true;
const utc = moment().utc();
const dateTime = options.time
? `${options.date} ${options.time}`
: options.date;
let utcDateTime;
const PREVIEW_TEMPLATE = `
<div class='preview'>
<span class='timezone'></span>
<span class='date-time'></span>
</div>
`;
let displayedTimezone;
function processElement($element, options = {}) {
clearTimeout(this.timeout);
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
);
$element
.html(DATE_TEMPLATE)
.attr("title", textPreview)
.attr("data-html-tooltip", `<div class="previews">${htmlPreview}</div>`)
.addClass("cooked-date")
.find(".relative-time")
.text(formatedDateTime);
this.timeout = setTimeout(() => processElement($element, options), 10000);
}
function _formatTimezone(timezone) {
return timezone
.replace("_", " ")
.replace("Etc/", "")
.split("/");
}
function _zoneWithoutPrefix(timezone) {
const parts = _formatTimezone(timezone);
return parts[1] || parts[0];
}
function _applyZoneToDateTime(dateTime, timezone) {
return moment.tz(dateTime, timezone).utc();
}
function _translateCalendarKey(time, 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", "")}]`;
}
}
function _calendarFormats(time) {
return {
sameDay: _translateCalendarKey(time, "today"),
nextDay: _translateCalendarKey(time, "tomorrow"),
lastDay: _translateCalendarKey(time, "yesterday"),
sameElse: "L"
};
}
function _isEqualZones(timezoneA, timezoneB) {
return (
moment.tz(timezoneA).utcOffset() === moment.tz(timezoneB).utcOffset()
);
}
function _applyFormatting(dateTime, displayedTimezone, options) {
const sameTimezone = _isEqualZones(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} (${_zoneWithoutPrefix(displayedTimezone)})`;
}
} else {
if (options.time) {
displayedTimezone = options.displayedTimezone || moment.tz.guess();
} else {
displayedTimezone =
options.displayedTimezone || options.timezone || moment.tz.guess();
}
dateTime = dateTime.format(options.format);
// 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 _zoneWithoutPrefix = timezone => {
const parts = _formatTimezone(timezone);
return parts[1] || parts[0];
};
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 compareZones = (timezoneA, timezoneB) => {
return (
moment.tz(timezoneA).utcOffset() === moment.tz(timezoneB).utcOffset()
);
};
const _applyFormatting = (dateTime, displayedTimezone, options) => {
const sameTimezone = compareZones(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);
if (options.displayedTimezone && !sameTimezone) {
dateTime = dateTime.replace("TZ", "");
dateTime = `${dateTime} (${_zoneWithoutPrefix(displayedTimezone)})`;
} else {
dateTime = dateTime.replace(
"TZ",
_formatTimezone(displayedTimezone).join(": ")
);
}
} else {
if (options.time) {
dateTime = dateTime.format(options.format);
dateTime = dateTime.format(options.format);
if (options.displayedTimezone && !sameTimezone) {
dateTime = dateTime.replace("TZ", "");
dateTime = `${dateTime} (${_zoneWithoutPrefix(displayedTimezone)})`;
} else {
dateTime = dateTime.replace(
"TZ",
_formatTimezone(displayedTimezone).join(": ")
);
}
if (!sameTimezone) {
dateTime = dateTime.replace("TZ", "");
dateTime = `${dateTime} (${_zoneWithoutPrefix(displayedTimezone)})`;
} else {
dateTime = dateTime.format(options.format);
if (!sameTimezone) {
dateTime = dateTime.replace("TZ", "");
dateTime = `${dateTime} (${_zoneWithoutPrefix(displayedTimezone)})`;
} else {
dateTime = dateTime.replace(
"TZ",
_zoneWithoutPrefix(displayedTimezone)
);
}
dateTime = dateTime.replace(
"TZ",
_zoneWithoutPrefix(displayedTimezone)
);
}
}
}
return dateTime;
};
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);
function _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);
};
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");
function _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}`;
};
return `${startRange} ${separator} ${endRange}`;
}
const _generatePreviews = (dateTime, displayedTimezone, options) => {
const previewedTimezones = [];
const watchingUserTimezone = moment.tz.guess();
const timezones = options.timezones.filter(
timezone => timezone !== watchingUserTimezone
);
function _generatePreviews(dateTime, displayedTimezone, options) {
const previewedTimezones = [];
const watchingUserTimezone = moment.tz.guess();
const timezones = options.timezones.filter(
timezone => timezone !== watchingUserTimezone
);
if (!compareZones(displayedTimezone, watchingUserTimezone)) {
previewedTimezones.push({
timezone: watchingUserTimezone,
current: true,
dateTime: options.time
? dateTime.tz(watchingUserTimezone).format("LLL")
: createDateTimeRange(dateTime, watchingUserTimezone)
});
}
if (
options.timezone &&
displayedTimezone === watchingUserTimezone &&
options.timezone !== displayedTimezone &&
!compareZones(displayedTimezone, options.timezone)
) {
timezones.unshift(options.timezone);
}
timezones.filter(z => z).forEach(timezone => {
if (compareZones(timezone, displayedTimezone)) {
return;
}
if (compareZones(timezone, watchingUserTimezone)) {
timezone = watchingUserTimezone;
}
previewedTimezones.push({
timezone,
dateTime: options.time
? dateTime.tz(timezone).format("LLL")
: createDateTimeRange(dateTime, timezone)
});
if (!_isEqualZones(displayedTimezone, watchingUserTimezone)) {
previewedTimezones.push({
timezone: watchingUserTimezone,
current: true,
dateTime: options.time
? dateTime.tz(watchingUserTimezone).format("LLL")
: _createDateTimeRange(dateTime, watchingUserTimezone)
});
}
if (!previewedTimezones.length) {
previewedTimezones.push({
timezone: "Etc/UTC",
dateTime: options.time
? dateTime.tz("Etc/UTC").format("LLL")
: createDateTimeRange(dateTime, "Etc/UTC")
});
if (
options.timezone &&
displayedTimezone === watchingUserTimezone &&
options.timezone !== displayedTimezone &&
!_isEqualZones(displayedTimezone, options.timezone)
) {
timezones.unshift(options.timezone);
}
timezones.filter(z => z).forEach(timezone => {
if (_isEqualZones(timezone, displayedTimezone)) {
return;
}
return _.uniq(previewedTimezones, "timezone");
};
if (_isEqualZones(timezone, watchingUserTimezone)) {
timezone = watchingUserTimezone;
}
const _generateTextPreview = previews => {
return previews
.map(preview => {
const formatedZone = _zoneWithoutPrefix(preview.timezone);
previewedTimezones.push({
timezone,
dateTime: options.time
? dateTime.tz(timezone).format("LLL")
: _createDateTimeRange(dateTime, timezone)
});
});
if (preview.dateTime.match(/TZ/)) {
return preview.dateTime.replace(/TZ/, formatedZone);
} else {
return `${formatedZone} ${preview.dateTime}`;
}
})
.join(", ");
};
if (!previewedTimezones.length) {
previewedTimezones.push({
timezone: "Etc/UTC",
dateTime: options.time
? dateTime.tz("Etc/UTC").format("LLL")
: _createDateTimeRange(dateTime, "Etc/UTC")
});
}
const _generateHtmlPreview = previews => {
const $htmlTooltip = $("<div></div>");
return _.uniq(previewedTimezones, "timezone");
}
const $previewTemplate = $(`
<div class='preview'>
<span class='timezone'></span>
<span class='date-time'></span>
</div>
`);
function _generateTextPreview(previews) {
return previews
.map(preview => {
const formatedZone = _zoneWithoutPrefix(preview.timezone);
previews.forEach(preview => {
const $template = $previewTemplate.clone();
if (preview.dateTime.match(/TZ/)) {
return preview.dateTime.replace(/TZ/, formatedZone);
} else {
return `${formatedZone} ${preview.dateTime}`;
}
})
.join(", ");
}
function _generateHtmlPreview(previews) {
return previews
.map(preview => {
const $template = $(PREVIEW_TEMPLATE);
if (preview.current) $template.addClass("current");
$template.find(".timezone").text(_zoneWithoutPrefix(preview.timezone));
$template.find(".date-time").text(preview.dateTime);
$htmlTooltip.append($template);
});
return $htmlTooltip.html();
};
return $template[0].outerHTML;
})
.join("");
}
$.fn.applyLocalDates = function() {
return this.each(function() {
const $element = $(this);