User:Gary/comments in local time.js: Difference between revisions

Content deleted Content added
f
Major cleanup of the code. It otherwise works the same. If something is majorly broken, then please ask an admin to undo this. For minor bugs, just post to the script's talk page.
Line 1:
/* eslint-disable complexity, max-statements */
/**
* COMMENTS IN LOCAL TIME
Line 10:
* [[Wikipedia:Comments in Local Time]]
*/
$(() => {
var CommentsInLocalTime, runScript;
/**
* Given a number, add a leading zero if necessary, so that the final number
* has two characters.
*
* @param {number} number Number
* @returns {string} The number with a leading zero, if necessary.
*/
function addLeadingZero(number) {
const numberArg = number;
 
if (numberArg < 10) {
CommentsInLocalTime = (function() {
return `0${numberArg}`;
var LocalComments, language;
}
 
return numberArg;
function CommentsInLocalTime() {}
}
 
function convertMonthToNumber(month) {
language = '';
return new Date(`${month} 1, 2001`).getMonth();
}
 
/**
LocalComments = {};
* Determine whether to use the singular or plural word, and use that.
*
* @param {string} term Original term
* @param {number} count Count of items
* @param {string} plural Pluralized term
* @returns {string} The word to use
*/
function pluralize(term, count, plural = null) {
let pluralArg = plural;
 
// No unique pluralized word is found, so just use a general one.
CommentsInLocalTime.settings = function() {
if (window.LocalComments != nullpluralArg) {
LocalCommentspluralArg = window.LocalComments`${term}s`;
}
 
// There's only one item, so just use the singular word.
/*
if (count Language=== 1) {
return term;
}
 
// There are multiple items, so use the plural word.
LOCALIZING THIS SCRIPT
return pluralArg;
To localize this script, change the terms below,
}
to the RIGHT of the colons, to the correct term used in that language.
 
class CommentsInLocalTime {
For example, in the French language,
constructor() {
this.language = '';
this.LocalComments = {};
 
'Today' : 'Today',/**
* Settings
*/
this.settings();
 
this.language = this.setDefaultSetting(
would be
'language',
this.LocalComments.language
);
 
// These values are also reflected in the documentation:
'Today' : "Aujourd'hui",
// https://en.wikipedia.org/wiki/Wikipedia:Comments_in_Local_Time#Default_settings
*/
this.setDefaultSetting({
return (LocalComments.language = {
/* relative termsdateDifference: */true,
Today dateFormat: 'Todaydmy',
Yesterday dayOfWeek: 'Yesterday'true,
Tomorrow dropDays: 'Tomorrow'0,
last dropMonths: 'last'0,
this timeFirst: 'this'true,
twentyFourHours: false,
});
}
 
adjustTime(originalTimestamp, search) {
/* days of the week */
let time = originalTimestamp.match(search);
Sunday: 'Sunday',
const [, oldHour, oldMinute, oldDay, oldMonth, oldYear] = time;
Monday: 'Monday',
Tuesday: 'Tuesday',
Wednesday: 'Wednesday',
Thursday: 'Thursday',
Friday: 'Friday',
Saturday: 'Saturday',
 
/*/ months of the year */Today
January:const 'January',today = new Date();
February: 'February',
March: 'March',
April: 'April',
May: 'May',
June: 'June',
July: 'July',
August: 'August',
September: 'September',
October: 'October',
November: 'November',
December: 'December',
 
/*/ difference words */Yesterday
const yesterday = new Date();
ago: 'ago',
'from now': 'from now',
 
yesterday.setDate(yesterday.getDate() - 1);
/* date phrases */
year: 'year',
years: 'years',
month: 'month',
months: 'months',
day: 'day',
days: 'days',
});
};
 
// Tomorrow
/*
const tomorrow = new Date();
APPLICATION
*/
 
tomorrow.setDate(tomorrow.getDate() + 1);
CommentsInLocalTime.init = function() {
/*
Settings
*/
var contentText, namespace, pageAction;
this.settings();
if (LocalComments.language == null) {
return false;
}
language = this.setDefaultSetting('language', LocalComments.language);
this.setDefaultSetting({
dateDifference: true,
dateFormat: 'dmy',
dayOfWeek: true,
dropDays: 0,
dropMonths: 0,
timeFirst: true,
twentyFourHours: false,
});
 
// Set the date entered.
/*
Endtime Settings= new Date();
time.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);
*/
time.setUTCHours(oldHour);
if (
time.setUTCMinutes(oldMinute);
mw.config.get('wgCanonicalNamespace') == '' ||
 
mw.config.get('wgCanonicalNamespace') == 'MediaWiki' ||
// A string matching the date pattern was found, but it cannot be
mw.config.get('wgCanonicalNamespace') == 'Special'
// converted to a Date object. Return it with no changes made.
)
if (Number.isNaN(time)) {
return;
return [originalTimestamp, ''];
}
 
// Determine the time offset.
const utcValue = (-1 * time.getTimezoneOffset()) / 60;
const utcOffset =
utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;
 
// Set the date bits to output.
const year = time.getFullYear();
const month = addLeadingZero(time.getMonth() + 1);
const day = time.getDate();
let hour = parseInt(time.getHours(), 10);
const minute = addLeadingZero(time.getMinutes());
 
// Output am or pm depending on the date.
let ampm = '';
 
if (this.LocalComments.twentyFourHours) {
hour = addLeadingZero(hour);
} else {
ampm = hour <= 11 ? ' am' : ' pm';
 
if (hour > 12) {
hour -= 12;
} else if (hour === 0) {
hour = 12;
}
}
 
// return 'today' or 'yesterday' if that is the case
let date;
 
if (
year === today.getFullYear() &&
month === addLeadingZero(today.getMonth() + 1) &&
day === today.getDate()
) {
date = this.language.Today;
} else if (
year === yesterday.getFullYear() &&
month === addLeadingZero(yesterday.getMonth() + 1) &&
day === yesterday.getDate()
) {
date = this.language.Yesterday;
} else if (
year === tomorrow.getFullYear() &&
month === addLeadingZero(tomorrow.getMonth() + 1) &&
day === tomorrow.getDate()
) {
date = this.language.Tomorrow;
} else {
// calculate day of week
const dayNames = [
this.language.Sunday,
this.language.Monday,
this.language.Tuesday,
this.language.Wednesday,
this.language.Thursday,
this.language.Friday,
this.language.Saturday,
];
const dayOfTheWeek = dayNames[time.getDay()];
let descriptiveDifference = '';
let last = '';
 
// Create a relative descriptive difference
if (this.LocalComments.dateDifference) {
({ descriptiveDifference, last } = this.createRelativeDate(
today,
time
));
}
 
const monthName = this.convertNumberToMonth(time.getMonth());
 
// format the date according to user preferences
let formattedDate = '';
 
switch (this.LocalComments.dateFormat.toLowerCase()) {
case 'dmy':
formattedDate = `${day} ${monthName} ${year}`;
 
break;
case 'mdy':
formattedDate = `${monthName} ${day}, ${year}`;
 
break;
default:
formattedDate = `${year}-${month}-${addLeadingZero(day)}`;
}
 
let formattedDayOfTheWeek = '';
 
if (this.LocalComments.dayOfWeek) {
formattedDayOfTheWeek = `, ${last}${dayOfTheWeek}`;
}
 
date = formattedDate + formattedDayOfTheWeek + descriptiveDifference;
}
 
const finalTime = `${hour}:${minute}${ampm}`;
let returnDate;
 
if (this.LocalComments.timeFirst) {
returnDate = `${finalTime}, ${date} (UTC${utcOffset})`;
} else {
returnDate = `${date}, ${finalTime} (UTC${utcOffset})`;
}
 
return [returnDate, time];
var disabled_urls = new Array('action=history'),
unique_url = false,
wikiPreview = new Array('action=edit', 'action=submit');
for (var i = 0; i < disabled_urls.length; i++) {
if (document.___location.href.indexOf(disabled_urls[i]) != -1) return;
}
 
convertNumberToMonth(number) {
for (var i = 0; i < wikiPreview.length; i++) {
return [
if (document.___location.href.indexOf(wikiPreview[i]) != -1)
unique_url = 'wikiPreview';this.language.January,
this.language.February,
this.language.March,
this.language.April,
this.language.May,
this.language.June,
this.language.July,
this.language.August,
this.language.September,
this.language.October,
this.language.November,
this.language.December,
][number];
}
 
createRelativeDate(today, time) {
var element_id = unique_url ? unique_url : 'bodyContent';
// Calculate time difference from today.
contentText = document.getElementById(element_id);
const millisecondsAgo = today.getTime() - time.getTime();
return this.replaceText(
contentText,
/(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/
);
};
 
let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));
CommentsInLocalTime.replaceText = function(node, search) {
let differenceWord = '';
var after,
afterMatch,let last = '';
 
before,
if (millisecondsAgo >= 0) {
beforeMatch,
differenceWord = this.language.ago;
child,
 
children,
length, if (daysAgo <= 7) {
last = `${this.language.last} `;
match,
matches, }
parent,} else {
differenceWord = this.language['from now'];
parentNodeName,
 
position,
span, if (daysAgo <= 7) {
last = `${this.language.this} `;
timeArray,
timestamp, }
value,
_i,
_len,
_results;
if (!node) {
return false;
}
if (node.nodeType === 3) {
parent = node.parentNode;
parentNodeName = parent.nodeName;
if (['CODE', 'PRE'].indexOf(parentNodeName) > -1) {
return false;
}
 
value = node.nodeValue;
// This method of computing the years & months is not exact. However, it's
matches = value.match(search);
// better than the previous method that used 1 January + delta days. That
if (matches != null) {
// was usually quite off because it mapped the second delta month to
match = matches[0];
// February, which has only 28 days. This method is usually not more than
position = value.search(search);
// one lengthday = match.toString()off.length;
 
beforeMatch = value.substring(0, position);
/**
afterMatch = value.substring(position + length);
* The number of months ago that we will display. It's not necessarily the
timeArray = this.adjustTime(match.toString(), search);
* total months ago.
timestamp = timeArray[1] ? timeArray[1].getTime() : '';
*
span = document.createElement('span');
* span.className@type = 'localcomments';{number}
*/
span.style.fontSize = '95%';
let monthsAgo = Math.floor((daysAgo / 365) * 12);
span.style.whiteSpace = 'nowrap';
 
span.setAttribute('timestamp', timestamp);
span.title = match;/**
* The total amount of time ago, in months.
span.appendChild(document.createTextNode(timeArray[0]));
parent = node.parentNode;*
* parent.replaceChild(span,@type node);{number}
*/
before = document.createElement('span');
const totalMonthsAgo = monthsAgo;
before.className = 'before-localcomments';
 
before.appendChild(document.createTextNode(beforeMatch));
/**
after = document.createElement('span');
* The number of years ago that we will display. It's not necessarily the
after.className = 'after-localcomments';
* total years ago.
after.appendChild(document.createTextNode(afterMatch));
*/
parent.insertBefore(before, span);
let yearsAgo = Math.floor(totalMonthsAgo / 12);
return parent.insertBefore(after, span.nextSibling);
 
if (totalMonthsAgo < this.LocalComments.dropMonths) {
yearsAgo = 0;
} else if (this.LocalComments.dropMonths > 0) {
monthsAgo = 0;
} else {
monthsAgo -= yearsAgo * 12;
}
 
} else {
if (daysAgo < this.LocalComments.dropDays) {
children = [];
child monthsAgo = node.childNodes[0];
while (child) {yearsAgo = 0;
} else if (this.LocalComments.dropDays > 0 && totalMonthsAgo >= 1) {
children.push(child);
childdaysAgo = child.nextSibling0;
} else {
daysAgo -= Math.floor((totalMonthsAgo * 365) / 12);
}
 
_results = [];
const descriptiveParts = [];
for (_i = 0, _len = children.length; _i < _len; _i++) {
 
child = children[_i];
if (yearsAgo > 0) {
_results.push(this.replaceText(child, search));
const fmtYears = `${yearsAgo} ${pluralize(
this.language.year,
yearsAgo,
this.language.years
)}`;
 
descriptiveParts.push(fmtYears);
}
return _results;
}
};
 
if (monthsAgo > 0) {
CommentsInLocalTime.adjustTime = function(originalTimestamp, search) {
const fmtMonths = `${monthsAgo} ${pluralize(
var ampm,
date this.language.month,
day monthsAgo,
this.language.months
dayNames,
dayOfTheWeek, )}`;
 
descriptiveDifference,
descriptiveParts.push(fmtMonths);
finalTime,
formattedDate,
formattedDayOfTheWeek,
hour,
last,
minute,
month,
monthName,
oldDay,
oldHour,
oldMinute,
oldMonth,
oldYear,
returnDate,
time,
today,
tomorrow,
utcOffset,
year,
yesterday,
_ref,
_ref1;
time = originalTimestamp.match(search);
(_ref = [time[1], time[2], time[3], time[4], time[5]]),
(oldHour = _ref[0]),
(oldMinute = _ref[1]),
(oldDay = _ref[2]),
(oldMonth = _ref[3]),
(oldYear = _ref[4]);
today = new Date();
yesterday = new Date();
tomorrow = new Date();
yesterday.setDate(yesterday.getDate() - 1);
tomorrow.setDate(tomorrow.getDate() + 1);
time = new Date();
time.setUTCFullYear(oldYear, this.convertMonthToNumber(oldMonth), oldDay);
time.setUTCHours(oldHour);
time.setUTCMinutes(oldMinute);
if (isNaN(time)) {
return [originalTimestamp, ''];
}
utcOffset = (-1 * time.getTimezoneOffset()) / 60;
utcOffset = utcOffset >= 0 ? '+' + utcOffset : '−' + Math.abs(utcOffset);
year = time.getFullYear();
month = this.addLeadingZero(time.getMonth() + 1);
day = time.getDate();
hour = parseInt(time.getHours());
minute = this.addLeadingZero(time.getMinutes());
ampm = '';
if (LocalComments.twentyFourHours) {
hour = this.addLeadingZero(hour);
} else {
ampm = hour <= 11 ? ' am' : ' pm';
if (hour > 12) {
hour -= 12;
} else if (hour === 0) {
hour = 12;
}
 
if (daysAgo > 0) {
const fmtDays = `${daysAgo} ${pluralize(
this.language.day,
daysAgo,
this.language.days
)}`;
 
descriptiveParts.push(fmtDays);
}
 
return {
descriptiveDifference: ` (${descriptiveParts.join(
', '
)} ${differenceWord})`,
last,
};
}
 
if (
replaceText(node, search) {
year === today.getFullYear() &&
if (!node) {
month === this.addLeadingZero(today.getMonth() + 1) &&
day === today.getDate()return;
) {
date = language['Today'];
} else if (
year === yesterday.getFullYear() &&
month === this.addLeadingZero(yesterday.getMonth() + 1) &&
day === yesterday.getDate()
) {
date = language['Yesterday'];
} else if (
year === tomorrow.getFullYear() &&
month === this.addLeadingZero(tomorrow.getMonth() + 1) &&
day === tomorrow.getDate()
) {
date = language['Tomorrow'];
} else {
dayNames = [
language['Sunday'],
language['Monday'],
language['Tuesday'],
language['Wednesday'],
language['Thursday'],
language['Friday'],
language['Saturday'],
];
dayOfTheWeek = dayNames[time.getDay()];
descriptiveDifference = '';
last = '';
if (LocalComments.dateDifference) {
(_ref1 = this.createRelativeDate(today, time)),
(descriptiveDifference = _ref1.descriptiveDifference),
(last = _ref1.last);
}
 
formattedDate = '';
// Check if this is a text node.
monthName = this.convertNumberToMonth(time.getMonth());
formattedDateif (node.nodeType === function(3) {
let parent = node.parentNode;
switch (LocalComments.dateFormat.toLowerCase()) {
 
case 'dmy':
const parentNodeName = parent.nodeName;
return day + ' ' + monthName + ' ' + year;
 
case 'mdy':
if (['CODE', 'PRE'].includes(parentNodeName)) {
return monthName + ' ' + day + ', ' + year;
default:return;
return year + '-' + month + '-' + this.addLeadingZero(day);
}
 
}.call(this);
formattedDayOfTheWeek const value = ''node.nodeValue;
const matches = value.match(search);
if (LocalComments.dayOfWeek) {
 
formattedDayOfTheWeek = ', ' + last + dayOfTheWeek;
// Stick with manipulating the DOM directly rather than using jQuery.
// I've got more than a 100% speed improvement afterward.
if (matches) {
// Only act on the first timestamp we found in this node. This is
// really a temporary fix for the situation in which there are two or
// more timestamps in the same node.
const [match] = matches;
const position = value.search(search);
const stringLength = match.toString().length;
const beforeMatch = value.substring(0, position);
const afterMatch = value.substring(position + stringLength);
const timeArray = this.adjustTime(match.toString(), search);
const timestamp = timeArray[1] ? timeArray[1].getTime() : '';
 
// Is the "timestamp" attribute used for microformats?
const span = document.createElement('span');
 
span.className = 'localcomments';
span.style.fontSize = '95%';
span.style.whiteSpace = 'nowrap';
span.setAttribute('timestamp', timestamp);
span.title = match;
span.append(document.createTextNode(timeArray[0]));
 
parent = node.parentNode;
parent.replaceChild(span, node);
 
const before = document.createElement('span');
 
before.className = 'before-localcomments';
before.append(document.createTextNode(beforeMatch));
 
const after = document.createElement('span');
 
after.className = 'after-localcomments';
after.append(document.createTextNode(afterMatch));
 
parent.insertBefore(before, span);
parent.insertBefore(after, span.nextSibling);
}
} else {
const children = [];
let child;
 
[child] = node.childNodes;
 
while (child) {
children.push(child);
child = child.nextSibling;
}
 
// Loop through children and run this func on it again, recursively.
children.forEach((child2) => {
this.replaceText(child2, search);
});
}
date = formattedDate + formattedDayOfTheWeek + descriptiveDifference;
}
finalTime = hour + ':' + minute + ampm;
if (LocalComments.timeFirst) {
returnDate = finalTime + ', ' + date + ' (UTC' + utcOffset + ')';
} else {
returnDate = date + ', ' + finalTime + ' (UTC' + utcOffset + ')';
}
return [returnDate, time];
};
 
run() {
CommentsInLocalTime.createRelativeDate = function(today, time) {
var daysAgo, if (
mw.config.get('wgCanonicalNamespace') === '' ||
descriptiveParts,
mw.config.get('wgCanonicalNamespace') === 'MediaWiki' ||
differenceWord,
mw.config.get('wgCanonicalNamespace') === 'Special'
fmtDays,
fmtMonths,) {
fmtYears, return;
last,
millisecondsAgo,
monthsAgo,
totalMonthsAgo,
yearsAgo;
millisecondsAgo = today.getTime() - time.getTime();
daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));
differenceWord = '';
last = '';
if (millisecondsAgo >= 0) {
differenceWord = language['ago'];
if (daysAgo <= 7) {
last = language['last'] + ' ';
}
 
} else {
differenceWordconst disabledUrls = language['from nowaction=history'];
 
if (daysAgo <= 7) {
// Check for disabled URLs.
last = language['this'] + ' ';
const isDisabledUrl = disabledUrls.some((disabledUrl) =>
document.___location.href.includes(disabledUrl)
);
 
if (isDisabledUrl) {
return;
}
}
monthsAgo = Math.floor((daysAgo / 365) * 12);
totalMonthsAgo = monthsAgo;
yearsAgo = Math.floor(monthsAgo / 12);
if (monthsAgo < LocalComments.dropMonths) {
yearsAgo = 0;
} else if (LocalComments.dropMonths > 0) {
monthsAgo = 0;
} else {
monthsAgo = monthsAgo - yearsAgo * 12;
}
if (daysAgo < LocalComments.dropDays) {
monthsAgo = 0;
yearsAgo = 0;
} else if (LocalComments.dropDays > 0) {
daysAgo = 0;
} else {
daysAgo = daysAgo - Math.floor((totalMonthsAgo * 365) / 12);
}
descriptiveParts = [];
if (yearsAgo > 0) {
fmtYears =
yearsAgo +
' ' +
this.pluralize(language['year'], yearsAgo, language['years']);
descriptiveParts.push(fmtYears);
}
if (monthsAgo > 0) {
fmtMonths =
monthsAgo +
' ' +
this.pluralize(language['month'], monthsAgo, language['months']);
descriptiveParts.push(fmtMonths);
}
if (daysAgo > 0) {
fmtDays =
daysAgo +
' ' +
this.pluralize(language['day'], daysAgo, language['days']);
descriptiveParts.push(fmtDays);
}
return {
descriptiveDifference:
' (' + descriptiveParts.join(', ') + ' ' + differenceWord + ')',
last: last,
};
};
 
const wikiPreview = ['action=edit', 'action=submit'];
/*
HELPERS
*/
 
// Check what ID we should use, to get the page's text.
CommentsInLocalTime.addLeadingZero = function(number) {
const hasUniqueUrl = wikiPreview.some((text) =>
if (number < 10) {
document.___location.href.includes(text)
number = '0' + number;
);
 
const elementId = hasUniqueUrl ? 'wikiPreview' : 'bodyContent';
const contentText = document.querySelector(`#${elementId}`);
 
this.replaceText(
contentText,
/(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/
);
}
return number;
};
 
setDefaultSetting(...args) {
CommentsInLocalTime.convertMonthToNumber = function(month) {
// There are no arguments.
return new Date(month + ' 1, 2001').getMonth();
if (args.length === 0) {
};
return false;
}
 
// The first arg is an object, so just set that data directly onto the
CommentsInLocalTime.convertNumberToMonth = function(number) {
// settings object. like {setting 1: true, setting 2: false}
return [
languageif (typeof args[0] === 'Januaryobject'],) {
language const ['February'settings], = args;
language['March'],
language['April'],
language['May'],
language['June'],
language['July'],
language['August'],
language['September'],
language['October'],
language['November'],
language['December'],
][number];
};
 
// Loop through each setting.
CommentsInLocalTime.pluralize = function(term, count, plural) {
Object.keys(settings).forEach((name) => {
if (plural === null) {
plural = term + 's'const value = settings[name];
}
if (count === 1) {
return term;
} else {
return plural;
}
};
 
if (typeof this.LocalComments[name] === 'undefined') {
CommentsInLocalTime.setDefaultSetting = function() {
this.LocalComments[name] = value;
var defaultSetting, name, settings;
}
if (!arguments.length) {
return false });
 
}
return settings;
if (typeof arguments[0] === 'object') {
settings = arguments[0];
for (name in settings) {
defaultSetting = settings[name];
if (LocalComments[name] == null) {
LocalComments[name] = defaultSetting;
}
}
 
return settings;
// The first arg is a string, so use the first arg as the settings key,
} else if (typeof arguments[0] === 'string') {
// and the second arg as the value to set it to.
name = arguments[0];
const [name, setting] = args;
defaultSetting = arguments[1];
 
if (LocalComments[name] == null) {
if (typeof this.LocalComments[name] === 'undefined') defaultSetting;{
this.LocalComments[name] = setting;
}
 
return LocalComments[name];
return this.LocalComments[name];
}
 
/**
* Set the script's settings.
*
* @returns {undefined}
*/
settings() {
// The user has set custom settings, so use those.
if (window.LocalComments) {
this.LocalComments = window.LocalComments;
}
 
/**
* Language
*
* LOCALIZING THIS SCRIPT
* To localize this script, change the terms below,
* to the RIGHT of the colons, to the correct term used in that language.
*
* For example, in the French language,
*
* 'Today' : 'Today',
*
* would be
*
* 'Today' : "Aujourd'hui",
*/
this.LocalComments.language = {
// Relative terms
Today: 'Today',
Yesterday: 'Yesterday',
Tomorrow: 'Tomorrow',
last: 'last',
this: 'this',
 
// Days of the week
Sunday: 'Sunday',
Monday: 'Monday',
Tuesday: 'Tuesday',
Wednesday: 'Wednesday',
Thursday: 'Thursday',
Friday: 'Friday',
Saturday: 'Saturday',
 
// Months of the year
January: 'January',
February: 'February',
March: 'March',
April: 'April',
May: 'May',
June: 'June',
July: 'July',
August: 'August',
September: 'September',
October: 'October',
November: 'November',
December: 'December',
 
// Difference words
ago: 'ago',
'from now': 'from now',
 
// Date phrases
year: 'year',
years: 'years',
month: 'month',
months: 'months',
day: 'day',
days: 'days',
};
}
};
 
returnconst commentsInLocalTime = new CommentsInLocalTime();
})();
 
commentsInLocalTime.run();
$(() => CommentsInLocalTime.init());
});