Last active
May 15, 2019 18:22
-
-
Save JeffreyMercado/63a853d49abba832fbc14aed6c249af2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.