« get me outta code hell

scratch-comment-redactor.js - scratch-userscripts - Handy userscripts for the Scratch website - issues/ideas: https://notabug.org/towerofnix/scratch-userscripts/issues
about summary refs log tree commit diff
path: root/scratch-comment-redactor.js
blob: 76c71902bb0249ae7b057f0b7f323afc92e5f986 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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})
}