Cook'd and Bomb'd Really Ignore Users

Really ignores ignored users

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Cook'd and Bomb'd Really Ignore Users
// @description Really ignores ignored users
// @namespace   https://github.com/insin/greasemonkey/
// @version     3
// @match       https://www.cookdandbombd.co.uk/forums/index.php?board*
// @match       https://www.cookdandbombd.co.uk/forums/index.php?topic*
// @match       https://www.cookdandbombd.co.uk/forums/index.php?action=post*
// @match       https://www.cookdandbombd.co.uk/forums/index.php?action=profile;area=lists;sa=ignore*
// @match       https://www.cookdandbombd.co.uk/forums/index.php?action=unread*
// @grant       GM.registerMenuCommand
// ==/UserScript==

const IGNORED_USERS_STORAGE = 'cab_ignoredUsers'

let config = {
  addIgnoreUserControlToPosts: true,
  hidePostsQuotingIgnoredUsers: true,
  hideTopicsCreatedByIgnoredUsers: true,
  showIgnoredPosts: false,
}

let posts = []

function addStyle(css) {
  let $style = document.createElement('style')
  $style.appendChild(document.createTextNode(css))
  document.head.appendChild($style)
}

function addIgnoredPostsStyle() {
  addStyle(`
    .cab_ignoredPost {
      display: none;
    }
    .cab_ignoredPost.cab_show {
      display: block;
      background-color: #ddd !important;
    }
  `)
}

function getIgnoredUsers() {
  return JSON.parse(localStorage[IGNORED_USERS_STORAGE] || '[]')
}

function storeIgnoredUsers(ignoredUsers) {
  localStorage[IGNORED_USERS_STORAGE] = JSON.stringify(ignoredUsers)
}

function toggleShowIgnoredPosts(showIgnoredPosts) {
  config.showIgnoredPosts = showIgnoredPosts
  posts.forEach(post => post.updateClassNames())
}

/**
 * Topics being hidden breaks the CSS nth-of-type striping.
 */
function reStripePosts() {
  let odd = true
  posts.forEach(post => {
    if (!post.isIgnored()) {
      post.$el.classList.toggle('odd', odd)
      post.$el.classList.toggle('even', !odd)
      odd = !odd
    } else {
      post.$el.classList.remove('odd')
      post.$el.classList.remove('even')
    }
  })
}

function TopicPage() {
  addIgnoredPostsStyle()

  let isLoggedIn = document.querySelector('#profile_menu_top') != null

  let ignoredUsers
  let ignoredUserIds
  let ignoredUserNames

  function setIgnoredUsers(ignoreList) {
    ignoredUsers = ignoreList
    ignoredUserIds = ignoredUsers.map(user => user.id)
    ignoredUserNames = ignoredUsers.map(user => user.name)
  }

  function configureIgnoreControl({$a, $span, userId, userName}) {
    let isUserIgnored = ignoredUserIds.includes(userId)
    $a.href = `https://www.cookdandbombd.co.uk/forums/index.php?action=profile;area=lists;sa=ignore&${isUserIgnored ? `unignore=${userId}` : `ignore=${userName}`}`
    $a.title = `${isUserIgnored ? 'Remove from' : 'Add to'} ignore list`
    $span.classList.toggle('delete', isUserIgnored)
    $span.classList.toggle('ignore', !isUserIgnored)
  }

  function Post($wrapper) {
    let $userLink = $wrapper.querySelector('div.poster h4 a')

    let userId = $userLink.href.match(/;u=(\d+)/)[1]
    let userName = $userLink.textContent
    let quotedUserNames = Array.from($wrapper.querySelectorAll('.post blockquote cite a')).map(
      $a => $a.textContent.match(/Quote from: (.+) on /)?.[1] || $a.textContent.match(/Quote from: (.+)/)?.[1]
    ).filter(Boolean)

    let api = {
      $el: $wrapper,
      isIgnored() {
        let isUserIgnored = ignoredUserIds.includes(userId)
        let quotesIgnoredUser = config.hidePostsQuotingIgnoredUsers && quotedUserNames.some(userName => ignoredUserNames.includes(userName))
        return isUserIgnored || quotesIgnoredUser
      },
      updateClassNames() {
        let isPostIgnored = api.isIgnored()
        $wrapper.classList.toggle('cab_ignoredPost', isPostIgnored)
        $wrapper.classList.toggle('cab_show', config.showIgnoredPosts && isPostIgnored)
      }
    }

    // Add an ignore/unignore link to user profiles in posts
    if (config.addIgnoreUserControlToPosts) {
      let $a = document.createElement('a')
      let $span = document.createElement('span')
      $span.className = 'main_icons centericon'
      $a.appendChild($span)
      let $li = document.createElement('li')
      $li.appendChild($a)
      configureIgnoreControl({$a, $span, userId, userName})
      let $profileIcons = $wrapper.querySelector('div.poster ol.profile_icons')

      // Logged-out users don't get a profile list item, so we'll add our own
      if (!$profileIcons) {
        let $insertProfileAfter =
          $wrapper.querySelector('div.poster .im_icons') ||
          $wrapper.querySelector('div.poster .blurb') ||
          $wrapper.querySelector('div.poster .icons')
        let $profile = document.createElement('li')
        $profile.className = 'profile'
        $profileIcons = document.createElement('ol')
        $profileIcons.className = 'profile'
        $profile.appendChild($profileIcons)
        $insertProfileAfter.insertAdjacentElement('afterend', $profile)
      }

      $profileIcons.appendChild($li)

      // For logged-out users, manage the ignore list independently
      if (!isLoggedIn) {
        $a.addEventListener('click', (e) => {
          e.preventDefault()
          // Get a fresh copy in case it's been changed in another tab
          let ignoredUsers = getIgnoredUsers()
          let index = ignoredUsers.findIndex(user => user.id === userId)
          if (index != -1) {
            ignoredUsers.splice(index, 1)
          }
          else {
            ignoredUsers.push({id: userId, name: userName})
          }
          setIgnoredUsers(ignoredUsers)
          storeIgnoredUsers(ignoredUsers)
          configureIgnoreControl({$a, $span, userId, userName})
          posts.forEach(post => post.updateClassNames())
          reStripePosts()
        })
      }
    }

    api.updateClassNames()
    return api
  }

  let postElements = Array.from(document.querySelectorAll('#forumposts > form > div.windowbg'))
  let oddBg = postElements[0] ? getComputedStyle(postElements[0]).backgroundColor : null
  let evenBg = postElements[1] ? getComputedStyle(postElements[1]).backgroundColor : null

  addStyle(`
    .cab_ignoredPost {
      display: none;
    }
    .cab_ignoredPost.cab_show {
      display: block;
      background-color: #ddd !important;
    }
    ${oddBg ? `#forumposts .windowbg.odd {
      background-color: ${oddBg};
    }` : ''}
    ${evenBg ? `#forumposts .windowbg.even {
      background-color: ${evenBg};
    }` : ''}
  `)

  setIgnoredUsers(getIgnoredUsers())
  posts = postElements.map(Post)
  reStripePosts()
  document.body.classList.add('cab_reallyIgnoreUsers')
}

function PostReplyPage() {
  let ignoredUserNames = getIgnoredUsers().map(user => user.name)

  function Post($wrapper) {
    let $userHeader = $wrapper.querySelector('h5')

    let userName = $userHeader?.textContent.match(/Posted by (.+)/)?.[1]
    let quotedUserNames = Array.from($wrapper.querySelectorAll('blockquote cite a')).map(
      $a => $a.textContent.match(/Quote from: (.+) on /)?.[1] || $a.textContent.match(/Quote from: (.+)/)?.[1]
    ).filter(Boolean)

    let api = {
      $el: $wrapper,
      isIgnored() {
        let isUserIgnored = ignoredUserNames.includes(userName)
        let quotesIgnoredUser = config.hidePostsQuotingIgnoredUsers && quotedUserNames.some(userName => ignoredUserNames.includes(userName))
        return isUserIgnored || quotesIgnoredUser
      },
      updateClassNames() {
        let isPostIgnored = api.isIgnored()
        $wrapper.classList.toggle('cab_ignoredPost', isPostIgnored)
        $wrapper.classList.toggle('cab_show', config.showIgnoredPosts && isPostIgnored)
      }
    }

    api.updateClassNames()
    return api
  }

  let postElements = Array.from(document.querySelectorAll('#recent div.windowbg'))
  let oddBg = postElements[0] ? getComputedStyle(postElements[0]).backgroundColor : null
  let evenBg = postElements[1] ? getComputedStyle(postElements[1]).backgroundColor : null

  addStyle(`
    .cab_ignoredPost {
      display: none;
    }
    .cab_ignoredPost.cab_show {
      display: block;
      background-color: #ddd !important;
    }
    ${oddBg ? `#recent .windowbg.odd {
      background-color: ${oddBg};
    }` : ''}
    ${evenBg ? `#recent .windowbg.even {
      background-color: ${evenBg};
    }` : ''}
  `)

  posts = postElements.map(Post)
  reStripePosts()
  document.body.classList.add('cab_reallyIgnoreUsers')
}

function IgnoreListPage() {
  let params = new URLSearchParams(location.search)

  // Automatically ignore a user if ignore=name is provided in the URL
  if (params.has('ignore')) {
    let $newIgnoreInput = document.querySelector('#new_ignore')
    $newIgnoreInput.value = params.get('ignore')
    $newIgnoreInput.form.submit()
    return
  }

  // Automatically unignore a user if unignore=id is provided in the URL
  if (params.has('unignore')) {
    let $removeLink = Array.from(document.querySelectorAll('.table_grid tr td:last-child a')).find(
      $a => $a.href.includes(`remove=${params.get('unignore')}`)
    )
    if ($removeLink) {
      $removeLink.click()
      return
    }
  }

  // Otherwise sync with the ignore list
  let ignoredUsers = Array.from(document.querySelectorAll('.table_grid tr td:first-child a')).map($a => ({
    id: $a.href.match(/;u=(\d+)/)[1],
    name: $a.textContent,
  }))
  storeIgnoredUsers(ignoredUsers)
}

function ForumPage() {
  addStyle(`
    #topic_container .windowbg.cab_ignoredUser {
      display: none;
    }
  `)

  let ignoredUserIds = getIgnoredUsers().map(user => user.id)

  for (let $topicRow of document.querySelectorAll('#topic_container > div')) {
    let $userLink = $topicRow.querySelector('.info .floatleft a')
    let userId = $userLink?.href.match(/;u=(\d+)/)?.[1]
    if (ignoredUserIds.includes(userId)) {
      $topicRow.classList.add('cab_ignoredUser')
    }
  }
}

if (location.search.includes('?action=profile;area=lists;sa=ignore')) {
  IgnoreListPage()
}
else if (location.search.includes('?action=unread') || location.search.includes('?board')) {
  if (config.hideTopicsCreatedByIgnoredUsers) {
    ForumPage()
  }
}
else if (!document.body.classList.contains('cab_reallyIgnoreUsers')) {
  let page = location.search.includes('?action=post') ? PostReplyPage : TopicPage
  if (typeof GM != 'undefined') {
    page()
    GM.registerMenuCommand('Toggle ignored post display', () => {
      toggleShowIgnoredPosts(!config.showIgnoredPosts)
    })
  }
  else {
    chrome.storage.local.get((storedConfig) => {
      Object.assign(config, storedConfig)
      page()
    })
    chrome.storage.onChanged.addListener((changes) => {
      if ('showIgnoredPosts' in changes) {
        toggleShowIgnoredPosts(changes['showIgnoredPosts'].newValue)
      }
    })
  }
}