From 5469861a6a7a8b95d2e1378d7d5f8d8aa19f3ead Mon Sep 17 00:00:00 2001 From: Florrie Date: Tue, 19 Jun 2018 20:41:36 -0300 Subject: Rename redactor.js -> anonymizer.js --- scratch-comment-anonymizer.js | 111 ++++++++++++++++++++++++++++++++++++++++++ scratch-comment-redactor.js | 111 ------------------------------------------ 2 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 scratch-comment-anonymizer.js delete mode 100644 scratch-comment-redactor.js diff --git a/scratch-comment-anonymizer.js b/scratch-comment-anonymizer.js new file mode 100644 index 0000000..76c7190 --- /dev/null +++ b/scratch-comment-anonymizer.js @@ -0,0 +1,111 @@ +// ==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('Anonymize')) + 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}) +} diff --git a/scratch-comment-redactor.js b/scratch-comment-redactor.js deleted file mode 100644 index 76c7190..0000000 --- a/scratch-comment-redactor.js +++ /dev/null @@ -1,111 +0,0 @@ -// ==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('Anonymize')) - 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}) -} -- cgit 1.3.0-6-gf8a5