mirror of
https://github.com/LalleSX/4chan-XZ.git
synced 2025-10-07 07:22:37 +02:00
174 lines
5.5 KiB
JavaScript
174 lines
5.5 KiB
JavaScript
import Callbacks from "../classes/Callbacks";
|
|
import Post from "../classes/Post";
|
|
import Index from "../General/Index";
|
|
import { g, Conf, d, doc } from "../globals/globals";
|
|
import $ from "../platform/$";
|
|
import { DAY, HOUR, MINUTE, SECOND } from "../platform/helpers";
|
|
|
|
/*
|
|
* decaffeinate suggestions:
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
* DS205: Consider reworking code to avoid use of IIFEs
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
|
*/
|
|
var RelativeDates = {
|
|
INTERVAL: 30000,
|
|
|
|
init() {
|
|
if (
|
|
(['index', 'thread', 'archive'].includes(g.VIEW) && Conf['Relative Post Dates'] && !Conf['Relative Date Title']) ||
|
|
Index.enabled
|
|
) {
|
|
this.flush();
|
|
$.on(d, 'visibilitychange PostsInserted', this.flush);
|
|
}
|
|
|
|
if (Conf['Relative Post Dates']) {
|
|
return Callbacks.Post.push({
|
|
name: 'Relative Post Dates',
|
|
cb: this.node
|
|
});
|
|
}
|
|
},
|
|
|
|
node() {
|
|
if (!this.info.date) { return; }
|
|
const dateEl = this.nodes.date;
|
|
if (Conf['Relative Date Title']) {
|
|
$.on(dateEl, 'mouseover', () => RelativeDates.hover(this));
|
|
return;
|
|
}
|
|
if (this.isClone) { return; }
|
|
|
|
// Show original absolute time as tooltip so users can still know exact times
|
|
// Since "Time Formatting" runs its `node` before us, the title tooltip will
|
|
// pick up the user-formatted time instead of 4chan time when enabled.
|
|
dateEl.title = dateEl.textContent;
|
|
|
|
return RelativeDates.update(this);
|
|
},
|
|
|
|
// diff is milliseconds from now.
|
|
relative(diff, now, date, abbrev) {
|
|
let number;
|
|
let unit = (() => {
|
|
if ((number = (diff / DAY)) >= 1) {
|
|
const years = now.getFullYear() - date.getFullYear();
|
|
let months = now.getMonth() - date.getMonth();
|
|
const days = now.getDate() - date.getDate();
|
|
if (years > 1) {
|
|
number = years - ((months < 0) || ((months === 0) && (days < 0)));
|
|
return 'year';
|
|
} else if ((years === 1) && ((months > 0) || ((months === 0) && (days >= 0)))) {
|
|
number = years;
|
|
return 'year';
|
|
} else if ((months = months + (12*years)) > 1) {
|
|
number = months - (days < 0);
|
|
return 'month';
|
|
} else if ((months === 1) && (days >= 0)) {
|
|
number = months;
|
|
return 'month';
|
|
} else {
|
|
return 'day';
|
|
}
|
|
} else if ((number = (diff / HOUR)) >= 1) {
|
|
return 'hour';
|
|
} else if ((number = (diff / MINUTE)) >= 1) {
|
|
return 'minute';
|
|
} else {
|
|
// prevent "-1 seconds ago"
|
|
number = Math.max(0, diff) / SECOND;
|
|
return 'second';
|
|
}
|
|
})();
|
|
|
|
const rounded = Math.round(number);
|
|
|
|
if (abbrev) {
|
|
unit = unit === 'month' ? 'mo' : unit[0];
|
|
} else {
|
|
if (rounded !== 1) { unit += 's'; } // pluralize
|
|
}
|
|
|
|
if (abbrev) { return `${rounded}${unit}`; } else { return `${rounded} ${unit} ago`; }
|
|
},
|
|
|
|
// Changing all relative dates as soon as possible incurs many annoying
|
|
// redraws and scroll stuttering. Thus, sacrifice accuracy for UX/CPU economy,
|
|
// and perform redraws when the DOM is otherwise being manipulated (and scroll
|
|
// stuttering won't be noticed), falling back to INTERVAL while the page
|
|
// is visible.
|
|
//
|
|
// Each individual dateTime element will add its update() function to the stale list
|
|
// when it is to be called.
|
|
stale: [],
|
|
flush() {
|
|
// No point in changing the dates until the user sees them.
|
|
if (d.hidden) { return; }
|
|
|
|
const now = new Date();
|
|
for (var data of RelativeDates.stale) { RelativeDates.update(data, now); }
|
|
RelativeDates.stale = [];
|
|
|
|
// Reset automatic flush.
|
|
clearTimeout(RelativeDates.timeout);
|
|
return RelativeDates.timeout = setTimeout(RelativeDates.flush, RelativeDates.INTERVAL);
|
|
},
|
|
|
|
hover(post) {
|
|
const {
|
|
date
|
|
} = post.info;
|
|
const now = new Date();
|
|
const diff = now - date;
|
|
return post.nodes.date.title = RelativeDates.relative(diff, now, date);
|
|
},
|
|
|
|
// `update()`, when called from `flush()`, updates the elements,
|
|
// and re-calls `setOwnTimeout()` to re-add `data` to the stale list later.
|
|
update(data, now) {
|
|
let abbrev, date;
|
|
const isPost = data instanceof Post;
|
|
if (isPost) {
|
|
({
|
|
date
|
|
} = data.info);
|
|
abbrev = false;
|
|
} else {
|
|
date = new Date(+data.dataset.utc);
|
|
abbrev = !!data.dataset.abbrev;
|
|
}
|
|
if (!now) { now = new Date(); }
|
|
const diff = now - date;
|
|
const relative = RelativeDates.relative(diff, now, date, abbrev);
|
|
if (isPost) {
|
|
for (var singlePost of [data].concat(data.clones)) {
|
|
singlePost.nodes.date.firstChild.textContent = relative;
|
|
}
|
|
} else {
|
|
data.firstChild.textContent = relative;
|
|
}
|
|
return RelativeDates.setOwnTimeout(diff, data);
|
|
},
|
|
|
|
setOwnTimeout(diff, data) {
|
|
const delay = diff < MINUTE ?
|
|
SECOND - ((diff + (SECOND / 2)) % SECOND)
|
|
: diff < HOUR ?
|
|
MINUTE - ((diff + (MINUTE / 2)) % MINUTE)
|
|
: diff < DAY ?
|
|
HOUR - ((diff + (HOUR / 2)) % HOUR)
|
|
:
|
|
DAY - ((diff + (DAY / 2)) % DAY);
|
|
return setTimeout(RelativeDates.markStale, delay, data);
|
|
},
|
|
|
|
markStale(data) {
|
|
if (RelativeDates.stale.includes(data)) { return; } // We can call RelativeDates.update() multiple times.
|
|
if (data instanceof Post && !g.posts.get(data.fullID)) { return; } // collected post.
|
|
if (data instanceof Element && !doc.contains(data)) { return; } // removed catalog reply.
|
|
return RelativeDates.stale.push(data);
|
|
}
|
|
};
|
|
export default RelativeDates;
|