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

Content deleted Content added
Undid revision 892614449 by Gary (talk)
fixes
 
(8 intermediate revisions by 2 users not shown)
Line 1:
/* eslint-disable complexity, max-statements */
/**
* COMMENTS IN LOCAL TIME
Line 30 ⟶ 29:
function convertMonthToNumber(month) {
return new Date(`${month} 1, 2001`).getMonth();
}
 
function getDates(time) {
const [, oldHour, oldMinute, oldDay, oldMonth, oldYear] = time;
 
// Today
const today = new Date();
 
// Yesterday
const yesterday = new Date();
 
yesterday.setDate(yesterday.getDate() - 1);
 
// Tomorrow
const tomorrow = new Date();
 
tomorrow.setDate(tomorrow.getDate() + 1);
 
// Set the date entered.
const newTime = new Date();
 
newTime.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);
newTime.setUTCHours(oldHour);
newTime.setUTCMinutes(oldMinute);
 
return { time: newTime, today, tomorrow, yesterday };
}
 
Line 86 ⟶ 111:
 
adjustTime(originalTimestamp, search) {
const { time, today, tomorrow, yesterday } = getDates(
let time = originalTimestamp.match(search);
originalTimestamp.match(search)
const [, oldHour, oldMinute, oldDay, oldMonth, oldYear] = time;
);
 
// Today
const today = new Date();
 
// Yesterday
const yesterday = new Date();
 
yesterday.setDate(yesterday.getDate() - 1);
 
// Tomorrow
const tomorrow = new Date();
 
tomorrow.setDate(tomorrow.getDate() + 1);
 
// Set the date entered.
time = new Date();
time.setUTCFullYear(oldYear, convertMonthToNumber(oldMonth), oldDay);
time.setUTCHours(oldHour);
time.setUTCMinutes(oldMinute);
 
// A string matching the date pattern was found, but it cannot be
Line 113 ⟶ 120:
return [originalTimestamp, ''];
}
 
const date = this.determineDateText({
time,
today,
tomorrow,
yesterday,
});
 
const { ampm, hour } = this.getHour(time);
const minute = addLeadingZero(time.getMinutes());
const finalTime = `${hour}:${minute}${ampm}`;
 
// Determine the time offset.
Line 119 ⟶ 137:
utcValue >= 0 ? `+${utcValue}` : `−${Math.abs(utcValue.toFixed(1))}`;
 
//const SetutcPart the= date bits to output.`(UTC${utcOffset})`;
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());
 
const returnDate = this.LocalComments.timeFirst
// Output am or pm depending on the date.
? `${finalTime}, ${date} ${utcPart}`
let ampm = '';
: `${date}, ${finalTime} ${utcPart}`;
 
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
));
}
 
return returnDate;
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];
}
 
Line 241 ⟶ 163:
}
 
createRelativeDatecreateDateText(today{ day, month, time, today, year }) {
// Calculate timeday differenceof from today.week
const millisecondsAgodayNames = today.getTime() - time.getTime();[
this.language.Sunday,
 
this.language.Monday,
let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));
this.language.Tuesday,
let differenceWord = '';
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 (millisecondsAgo >= 0) {
if differenceWord = (this.languageLocalComments.ago;dateDifference) {
({ descriptiveDifference, last } = this.createRelativeDate(
today,
time
));
}
 
const monthName = this.convertNumberToMonth(time.getMonth());
if (daysAgo <= 7) {
last = `${this.language.last} `;
}
} else {
differenceWord = this.language['from now'];
 
// Format ifthe (daysAgodate <=according 7)to {user preferences
let lastformattedDate = `${this.language.this} `'';
 
}
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)}`;
}
 
const formattedDayOfTheWeek = this.LocalComments.dayOfWeek
// This method of computing the years & months is not exact. However, it's
? `, ${last}${dayOfTheWeek}`
// better than the previous method that used 1 January + delta days. That
: '';
// was usually quite off because it mapped the second delta month to
 
return `${formattedDate}${formattedDayOfTheWeek}${descriptiveDifference}`;
}
 
/**
* Create relative date data.
*
* @param {Date} today Today
* @param {Date} time The timestamp from a comment
* @returns {Object.<string, *>} Relative date data
*/
createRelativeDate(today, time) {
/**
* The time difference from today, in milliseconds.
*
* @type {number}
*/
const millisecondsAgo = today.getTime() - time.getTime();
 
/**
* The number of days ago, that we will display. It's not necessarily the
* total days ago.
*
* @type {number}
*/
let daysAgo = Math.abs(Math.round(millisecondsAgo / 1000 / 60 / 60 / 24));
const { differenceWord, last } = this.relativeText({
daysAgo,
millisecondsAgo,
});
 
// This method of computing the years and months is not exact. However,
// it's better than the previous method that used 1 January + delta days.
// That was usually quite off because it mapped the second delta month to
// February, which has only 28 days. This method is usually not more than
// one day off, except perhaps over very distant dates.
 
/**
* The number of months ago, that we will display. It's not necessarily the
* the total months ago.
*
* @type {number}
Line 287 ⟶ 262:
* The number of years ago that we will display. It's not necessarily the
* total years ago.
*
* @type {number}
*/
let yearsAgo = Math.floor(totalMonthsAgo / 12);
Line 309 ⟶ 286:
const descriptiveParts = [];
 
// There is years text to add.
if (yearsAgo > 0) {
descriptiveParts.push(
const fmtYears = `${yearsAgo} ${pluralize(
this.language.year,`${yearsAgo} ${pluralize(
yearsAgo this.language.year,
this.language.years yearsAgo,
)}`; this.language.years
)}`
 
descriptiveParts.push(fmtYears);
}
 
// There is months text to add.
if (monthsAgo > 0) {
descriptiveParts.push(
const fmtMonths = `${monthsAgo} ${pluralize(
this.language.month,`${monthsAgo} ${pluralize(
monthsAgo this.language.month,
this.language.months monthsAgo,
)}`; this.language.months
)}`
 
descriptiveParts.push(fmtMonths);
}
 
// There is days text to add.
if (daysAgo > 0) {
descriptiveParts.push(
const fmtDays = `${daysAgo} ${pluralize(
this.language.day,`${daysAgo} ${pluralize(
daysAgo this.language.day,
this.language.days daysAgo,
)}`; this.language.days
)}`
 
descriptiveParts.push(fmtDays);
}
 
Line 345 ⟶ 325:
last,
};
}
 
determineDateText({ time, today, tomorrow, yesterday }) {
// Set the date bits to output.
const year = time.getFullYear();
const month = addLeadingZero(time.getMonth() + 1);
const day = time.getDate();
 
// Return 'today' or 'yesterday' if that is the case
if (
year === today.getFullYear() &&
month === addLeadingZero(today.getMonth() + 1) &&
day === today.getDate()
) {
return this.language.Today;
}
 
if (
year === yesterday.getFullYear() &&
month === addLeadingZero(yesterday.getMonth() + 1) &&
day === yesterday.getDate()
) {
return this.language.Yesterday;
}
 
if (
year === tomorrow.getFullYear() &&
month === addLeadingZero(tomorrow.getMonth() + 1) &&
day === tomorrow.getDate()
) {
return this.language.Tomorrow;
}
 
return this.createDateText({ day, month, time, today, year });
}
 
getHour(time) {
let ampm;
let hour = Number.parseInt(time.getHours(), 10);
 
if (this.LocalComments.twentyFourHours) {
ampm = '';
hour = addLeadingZero(hour);
} else {
// Output am or pm depending on the date.
ampm = hour <= 11 ? ' am' : ' pm';
 
if (hour > 12) {
hour -= 12;
} else if (hour === 0) {
hour = 12;
}
}
 
return { ampm, hour };
}
 
relativeText({ daysAgo, millisecondsAgo }) {
let differenceWord = '';
let last = '';
 
// The date is in the past.
if (millisecondsAgo >= 0) {
differenceWord = this.language.ago;
 
if (daysAgo <= 7) {
last = `${this.language.last} `;
}
 
// The date is in the future.
} else {
differenceWord = this.language['from now'];
 
if (daysAgo <= 7) {
last = `${this.language.this} `;
}
}
 
return { differenceWord, last };
}
 
Line 354 ⟶ 413:
// Check if this is a text node.
if (node.nodeType === 3) {
// Don't continue if this text node's parent tag is one of these.
let parent = node.parentNode;
if (['CODE', 'PRE'].includes(node.parentNode.nodeName)) {
 
const parentNodeName = parent.nodeName;
 
if (['CODE', 'PRE'].includes(parentNodeName)) {
return;
}
Line 365 ⟶ 421:
const matches = value.match(search);
 
// 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 for
// reallythe arare temporaryoccassion fixthat forthere theis situationmore inthan whichone theretimestamp arein two orthe
// moresame timestamps in the sametext 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);
 
// Grab the text content before and after the matching timestamp,
const before = document.createElement('span');
// which we'll then wrap in their own SPAN nodes.
const beforeMatch = value.slice(0, position);
const afterMatch = value.slice(position + stringLength);
const returnDate = this.adjustTime(match.toString(), search);
 
// Create the code to display the new local comments content.
before.className = 'before-localcomments';
const $span = $(
before.append(document.createTextNode(beforeMatch));
`<span class="localcomments" style="font-size: 95%;" title="${match}">${returnDate}</span>`
);
 
// Replace the existing text node in the page with our new local
const after = document.createElement('span');
// comments node.
$(node).replaceWith($span);
 
// Replace the text content that appears before the timestamp.
after.className = 'after-localcomments';
if (beforeMatch) {
after.append(document.createTextNode(afterMatch));
$span.before(
`<span class="before-localcomments">${beforeMatch}</span>`
);
}
 
// Replace the text content that appears after the timestamp.
parent.insertBefore(before, span);
parent.insertBefore(after,if span.nextSibling(afterMatch); {
$span.after(
`<span class="after-localcomments">${afterMatch}</span>`
);
}
}
} else {
Line 425 ⟶ 478:
run() {
if (
['', 'MediaWiki', 'Special'].includes(
mw.config.get('wgCanonicalNamespace') === '' ||
mw.config.get('wgCanonicalNamespace') === 'MediaWiki' ||
)
mw.config.get('wgCanonicalNamespace') === 'Special'
) {
return;
}
 
const disabledUrls = ['action=history'];
 
// Check for disabled URLs.
const isDisabledUrl = disabledUrls['action=history'].some((disabledUrl) =>
document.___location.href.includes(disabledUrl)
);
Line 442 ⟶ 493:
return;
}
 
const wikiPreview = ['action=edit', 'action=submit'];
 
// Check what ID we should use, to get the page's text.
const hasUniqueUrl = wikiPreview.some((text) =>
document.___location.href.includes(text)
);
 
const elementId = hasUniqueUrl ? 'wikiPreview' : 'bodyContent';
const contentText = document.querySelector(`#${elementId}`);
 
this.replaceText(
document.querySelector('.mw-body-content .mw-parser-output'),
contentText,
/(\d{1,2}):(\d{2}), (\d{1,2}) ([A-Z][a-z]+) (\d{4}) \(UTC\)/
);