Cook'd and Bomb'd Really Ignore Users

Really ignores ignored users

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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)
      }
    })
  }
}