Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
// ==UserScript==
// @version 0.2
// @name Stack Exchange Timeline/Revisions links
// @description Adds timeline and revisions links to all applicable Stack Exchange posts
// @author Jeff Mercado
// @namespace https://stackoverflow.com/users/390278/jeff-mercado
// @include http*://stackoverflow.com/*
// @include http*://*.stackoverflow.com/*
// @include http*://askubuntu.com/*
// @include http*://*.askubuntu.com/*
// @include http*://superuser.com/*
// @include http*://*.superuser.com/*
// @include http*://serverfault.com/*
// @include http*://*.serverfault.com/*
// @include http*://mathoverflow.net/*
// @include http*://*.mathoverflow.net/*
// @include http*://stackapps.com/*
// @include http*://*.stackapps.com/*
// @include http*://*.stackexchange.com/*
// @exclude http*://chat.*.com/*
// ==/UserScript==
// this serves only to avoid embarassing mistakes caused by inadvertently loading this script onto a page that isn't a Stack Exchange page
let isSEsite = false;
for (let s of document.querySelectorAll('script')) isSEsite = isSEsite||/StackExchange\.ready\(/.test(s.textContent);
// don't bother running this if this isn't a scripted SE page
if (!isSEsite || typeof StackExchange === 'undefined' ) {
return;
}
function with_jquery(f) {
let script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = `
if (window.jQuery) (${f.toString()})(window.jQuery);
//# sourceURL=${encodeURI(GM_info.script.namespace.replace(/\/?$/, '/'))}${encodeURIComponent(GM_info.script.name)}
`; // make this easier to debug
document.body.appendChild(script);
}
with_jquery(($) => {
function main() {
let links = [
new Link('timeline', 'Δ'),
new Link('revisions', 'ℜ')
];
$(() => {
let pageHandler = getPageHandler(document);
if (!pageHandler) return;
pageHandler.getPosts().each(function() {
let postHandler = getPostHandler(pageHandler, this);
if (!postHandler) return;
postHandler.addLinks(links);
});
});
}
function getPageHandler(container) {
if ($('body', container).hasClass('question-page')) return new QuestionPageHandler(container);
return null;
}
function getPostHandler(pageHandler, container) {
if ($(container).hasClass('question')) return new QuestionPostHandler(pageHandler, container);
if ($(container).hasClass('answer')) return new AnswerPostHandler(pageHandler, container);
return null;
}
class Link {
constructor(name, shortName) {
this.name = name;
this.shortName = shortName;
}
getUrl(id) {
return `/posts/${id}/${this.name}`;
}
}
class PageHandler {
constructor(container) {
this.container = container;
}
getPosts() {
return $(this.container).find(this.postSelector);
}
getLabel(link) {
return link.name;
}
}
class QuestionPageHandler extends PageHandler {
constructor(container) {
super(container);
}
get postSelector() {
return 'div.question, div.answer';
}
}
class PostHandler {
constructor(pageHandler, container) {
this.pageHandler = pageHandler;
this.container = container;
}
getId() {
return $(this.container).data(this.idSelector);
}
addLinks(links) {
let editLink = $(this.container).find('.edit-post, .suggest-edit-post, *[id^="edit-pending-"]');
let separator = editLink.prev('.lsep');
let lastLink = editLink;
for (let link of links) {
if (this.shouldInsertLink(link)) {
let newLink = this.createLink(link);
lastLink.after(separator.clone(), newLink);
lastLink = newLink;
}
}
}
shouldInsertLink(link) {
if (link.name === 'revisions' && this.hasModifications()) {
return false;
}
return true;
}
hasModifications() {
// if modified, there will be an extra post signature, the edit signature
return this.getContent().find('.post-signature').length > 1;
}
getContent() {
return $(this.contentSelector, this.container);
}
createLink(link) {
return $('<a></a>', {
href: link.getUrl(this.getId()),
class: `${link.name}-link`,
title: this.getTitle(link),
text: this.getLabel(link)
});
}
getTitle(link) {
return `view the ${link.name} for this ${this.containerName}`;
}
getLabel(link) {
return this.pageHandler.getLabel(link);
}
}
class QuestionPostHandler extends PostHandler {
constructor(pageHandler, container) {
super(pageHandler, container);
}
get containerName() {
return 'post';
}
get idSelector() {
return 'questionid';
}
get contentSelector() {
return '.postcell';
}
}
class AnswerPostHandler extends PostHandler {
constructor(pageHandler, container) {
super(pageHandler, container);
}
get containerName() {
return 'answer';
}
get idSelector() {
return 'answerid';
}
get contentSelector() {
return '.answercell';
}
}
main();
});
@CollinChaffin

This comment has been minimized.

Copy link

CollinChaffin commented May 15, 2019

FYI, this won't be automatically installable to test in Tampermonkey unless you rename this to .user.js. Otherwise it is not detected properly and leaves it to the user to paste into their own local script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.