Created
May 7, 2018 21:48
-
-
Save towerofnix/445a04615c48e18651d15f06f01ec128 to your computer and use it in GitHub Desktop.
Anonymizes Scratch comments
This file contains hidden or 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== | |
// @name Scratch Comment Redactor | |
// @namespace https://towerofnix.github.io | |
// @match *://scratch.mit.edu/* | |
// @grant none | |
// ==/UserScript== | |
const usersSymbol = Symbol() | |
const replaceText = function(el, newText) { | |
while (el.firstChild) { | |
el.firstChild.remove() | |
} | |
el.appendChild(document.createTextNode(newText)) | |
} | |
const redactCommentThread = function(comment, usernameToRedact) { | |
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' | |
let topThread = comment | |
while (topThread && !topThread.classList.contains('top-level-reply')) { | |
topThread = topThread.parentElement | |
} | |
if (!topThread) { | |
console.error('Could not get the thread element, sorry :(') | |
console.info(comment) | |
return | |
} | |
if (!(usersSymbol in topThread)) { | |
topThread[usersSymbol] = {} // Mapping of username -> descriptor | |
} | |
const users = topThread[usersSymbol] | |
if (!users.hasOwnProperty(usernameToRedact)) { | |
const index = Object.keys(users).length | |
users[usernameToRedact] = { | |
realUsername: usernameToRedact, | |
fakeUsername: `[User ${alphabet[index]}]`, | |
hue: Math.round(360 / 7 * index) | |
} | |
} | |
const descriptor = users[usernameToRedact] | |
const allComments = [comment, ...topThread.querySelectorAll('.reply .comment')] | |
for (const comment of allComments) { | |
// Redact bold "author" username at top of comment | |
const authorUsernameEl = comment.querySelector('.info .name a') | |
const authorUsername = authorUsernameEl.innerText.trim() | |
if (authorUsername === usernameToRedact) { | |
replaceText(authorUsernameEl, descriptor.fakeUsername) | |
// Also redact profile picture: | |
const authorAvatarEl = comment.querySelector('#comment-user') | |
const img = authorAvatarEl.querySelector('img') | |
if (img) { | |
authorAvatarEl.querySelector('img').remove() | |
const fakeProfilePicture = document.createElement('div') | |
Object.assign(fakeProfilePicture.style, { | |
float: 'left', display: 'block', marginRight: '10px', | |
width: '45px', height: '45px', | |
backgroundColor: `hsl(${descriptor.hue}deg, 100%, 50%)` | |
}) | |
authorAvatarEl.appendChild(fakeProfilePicture) | |
} | |
} | |
// Redact "@user" replies | |
const a = comment.querySelector('.content a') | |
if (a && a.innerText.trim() === '@' + usernameToRedact) { | |
replaceText(a, '@' + descriptor.fakeUsername) | |
} | |
} | |
} | |
// Button-adder | |
const commentsContainer = document.querySelector('#comments ul.comments') | |
if (commentsContainer) { | |
const observer = new MutationObserver(mutations => { | |
for (const mutation of mutations) { | |
for (const addedNode of mutation.addedNodes) { | |
if (typeof addedNode.classList === 'undefined') continue | |
if (addedNode.classList.contains('top-level-reply') === false) continue | |
const topComment = addedNode | |
const comments = [topComment, ...topComment.querySelectorAll('.reply .comment')] | |
for (const comment of comments) { | |
const usernameEl = comment.querySelector('.info .name a') | |
if (usernameEl) { | |
const actionContainer = comment.querySelector('.actions-wrap') | |
const span = document.createElement('span') | |
const username = usernameEl.innerText.trim() | |
span.classList.add('actions', 'report') | |
span.appendChild(document.createTextNode('Redact')) | |
const handler = () => { | |
redactCommentThread(topComment, username) | |
replaceText(span, 'Redacted') | |
span.style.cursor = 'default' | |
span.removeEventListener('click', handler) | |
} | |
span.addEventListener('click', handler) | |
actionContainer.appendChild(span) | |
} | |
} | |
} | |
} | |
}) | |
observer.observe(commentsContainer, {childList: true}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment