Improve comment reading experience, hide certain comments, sort featured comments by reaction count or reply count, and more.
- // ==UserScript==
- // @name bangumi-comment-enhance
- // @version 0.2.4
- // @description Improve comment reading experience, hide certain comments, sort featured comments by reaction count or reply count, and more.
- // @author Flynn Cao
- // @namespace https://flynncao.uk/
- // @match https://bangumi.tv/*
- // @match https://chii.in/*
- // @match https://bgm.tv/*
- // @include /^https?:\/\/(((fast\.)?bgm\.tv)|chii\.in|bangumi\.tv)*/
- // @license MIT
- // ==/UserScript==
- 'use strict'
- // https://www.iconfont.cn/collections/detail?spm=a313x.user_detail.i1.dc64b3430.57e63a81itWm4A&cid=12086
- const Icons = {
- answerSheet:
- '<svg t="1741855047626" class="icon" viewBox="0 0 #### ####" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2040" width="256" height="256"><path d="M188.8 135.7c-29.7 0-53.8 24.1-53.8 53.7v644.7c0 29.7 24.1 53.7 53.8 53.7h645.4c29.7 0 53.8-24.1 53.8-53.7V189.4c0-29.7-24.1-53.7-53.8-53.7H188.8z m-13-71.1h671.5c61.8 0 111.9 50.1 111.9 111.8v670.8c0 61.7-50.1 111.8-111.9 111.8H175.8C114 959 63.9 909 63.9 847.2V176.4c0-61.8 50.1-111.8 111.9-111.8z m0 0" fill="#333333" p-id="2041"></path><path d="M328 328h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36zM556 332h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36zM784 332h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36z" fill="#333333" p-id="2042"></path><path d="M328 546h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36zM556 550h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36zM784 550h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36z" fill="#333333" p-id="2043"></path><path d="M328 764h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36zM556 768h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36zM784 768h-88c-19.8 0-36-16.2-36-36s16.2-36 36-36h88c19.8 0 36 16.2 36 36s-16.2 36-36 36z" fill="#333333" p-id="2044"></path></svg>',
- sorting:
- '<svg t="1741855109866" class="icon" viewBox="0 0 #### ####" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2338" width="256" height="256"><path d="M375 898c-19.8 0-36-16.2-36-36V162c0-19.8 16.2-36 36-36s36 16.2 36 36v700c0 19.8-16.2 36-36 36z" fill="#333333" p-id="2339"></path><path d="M398.2 889.6c-15.2 12.7-38 10.7-50.7-4.4L136.6 633.9c-12.7-15.2-10.7-38 4.4-50.7 15.2-12.7 38-10.7 50.7 4.4l210.8 251.3c12.8 15.2 10.8 38-4.3 50.7zM649 126c19.8 0 36 16.2 36 36v700c0 19.8-16.2 36-36 36s-36-16.2-36-36V162c0-19.8 16.2-36 36-36z" fill="#333333" p-id="2340"></path><path d="M625.8 134.4c15.2-12.7 38-10.7 50.7 4.4l210.8 251.3c12.7 15.2 10.7 38-4.4 50.7-15.2 12.7-38 10.7-50.7-4.4L621.4 185.1c-12.7-15.2-10.7-38 4.4-50.7z" fill="#333333" p-id="2341"></path></svg>',
- font: '<svg t="1741855156691" class="icon" viewBox="0 0 #### ####" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2635" width="256" height="256"><path d="M859 201H165c-19.8 0-36-16.2-36-36s16.2-36 36-36h694c19.8 0 36 16.2 36 36s-16.2 36-36 36z" fill="#585757" p-id="2636"></path><path d="M476 859V165c0-19.8 16.2-36 36-36s36 16.2 36 36v694c0 19.8-16.2 36-36 36s-36-16.2-36-36z" fill="#585757" p-id="2637"></path></svg>',
- gear: '<svg t="1741861365461" class="icon" viewBox="0 0 #### ####" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2783" data-darkreader-inline-fill="" width="256" height="256"><path d="M594.9 64.8c36.8-0.4 66.9 29.1 67.3 65.9v7.8c0 38.2 31.5 69.4 70.2 69.4 12.3 0 24.5-3.3 35-9.3l7.1-4.1c10.3-5.9 22.1-9 33.9-9 23.9 0 46.2 12.5 58.3 32.8L949.9 359c18.7 31.6 7.6 71.9-24.6 90.1l-6.9 3.9c-34 19.2-45.7 61.2-26.4 93.8 6.1 10.3 14.9 18.9 25.4 24.8l7 3.9c32.3 18 43.6 58.5 24.8 90.2L866 806.3c-9.1 15.2-23.8 26.2-41 30.6-17.1 4.4-35.3 2.2-50.7-6.4l-7-3.9c-21.9-12.2-48.5-12.4-70.6-0.4-10.7 5.9-19.7 14.5-25.9 25-6.1 10.4-9.4 22.1-9.3 33.8v7.8c0.1 17.8-7.2 34.7-20 47.1-12.6 12.2-29.6 19-47.2 19H428c-36.6 0.3-66.7-29-67.2-65.5l-0.1-7.8c-0.1-18.4-7.6-36-20.8-48.8-22.5-22-56.9-26.5-84.3-10.9l-7 4.1c-10.3 5.8-22 8.9-33.8 8.9-23.9 0-46.1-12.4-58.2-32.8L73.2 665.2c-8.9-15.1-11.3-33.2-6.7-50.1 4.6-16.9 15.8-31.3 31.2-39.8l6.8-3.9c16.2-9 28.2-24.2 33.1-42.1 4.9-17.4 2.4-36.1-6.9-51.6-6.2-10.4-15.1-19-25.7-24.9l-6.9-3.9c-15.5-8.4-27-22.8-31.7-39.8-4.7-17-2.3-35.2 6.7-50.4L156.3 218c9-15.1 23.8-26.2 41-30.6 17.1-4.4 35.3-2.1 50.7 6.5l7.1 3.9c21.9 12.3 48.6 12.5 70.7 0.5 10.8-5.9 19.8-14.6 26-25.1 6.1-10.4 9.3-22.2 9.2-34.1v-7.9c-0.2-17.8 7-34.8 19.8-47.2 12.6-12.3 29.7-19.1 47.5-19.1h166.6z m-163.2 71c-3.1 0-6.1 1.2-8.4 3.3-1.9 1.8-2.9 4.2-2.9 6.8l0.1 7.6c0.2 21.2-5.4 42-16.3 60.3a120.02 120.02 0 0 1-45.2 43.7c-37.4 20.4-82.6 20.2-119.7-0.7l-6.8-3.8c-2.8-1.6-6.1-2-9.2-1.2-2.8 0.7-5.3 2.5-6.8 5l-80 135.1c-2.7 4.5-1.1 10.2 4.1 13l6.7 3.7c18.6 10.3 34 25.3 44.7 43.4 16.3 27.6 20.6 59.9 12.1 90.8-8.5 30.8-29 56.9-56.9 72.5l-6.6 3.7c-5 2.9-6.6 8.5-3.9 12.9l80 135.1c1.9 3.2 5.7 5.3 10 5.3 2.1 0 4.3-0.5 6.1-1.6l6.8-3.8c18.1-10.3 38.8-15.8 59.9-15.8 31.8 0 62 12.3 84.7 34.4 23 22.5 35.9 52.6 36 84.7v7.5c0 5.2 4.9 9.9 11.3 9.9h160c3.2 0 6.2-1.2 8.3-3.3 1.8-1.7 2.9-4.2 2.9-6.7v-7.5c-0.1-20.9 5.6-41.6 16.4-59.8 10.8-18.3 26.4-33.4 45.1-43.7 37.3-20.4 82.4-20.2 119.5 0.6l6.7 3.8c2.8 1.5 6.1 1.9 9.2 1.1 2.8-0.7 5.3-2.5 6.8-5l80-135c2.7-4.5 1.1-10.2-4-13l-6.7-3.7c-18.4-10.2-33.7-25.2-44.4-43.3-33.8-57.1-13.4-130.5 45-163.5l6.6-3.7c5.1-2.9 6.6-8.5 3.9-13l-79.9-135.1c-2.2-3.4-6-5.4-10-5.3-2.1 0-4.3 0.5-6.1 1.6l-6.8 3.8c-18.3 10.5-39.1 16-60.2 16-66.5 0.2-120.6-53.5-120.8-119.9v-7.5c0-5.3-4.8-10-11.3-10l-160 0.3z m-3.4-15.5" p-id="2784"></path><path d="M512 584c39.8 0 72-32.2 72-72s-32.2-72-72-72-72 32.2-72 72 32.2 72 72 72z m0 72c-79.5 0-144-64.5-144-144s64.5-144 144-144 144 64.5 144 144-64.5 144-144 144z m0 0" p-id="2785"></path></svg>',
- }
- const NAMESPACE = 'BangumiCommentEnhance'
- const Storage = {
- set(key, value) {
- localStorage.setItem(`${NAMESPACE}_${key}`, JSON.stringify(value))
- },
- get(key) {
- const value = localStorage.getItem(`${NAMESPACE}_${key}`)
- return value ? JSON.parse(value) : undefined
- },
- async init(settings) {
- const keys = Object.keys(settings)
- for (const key of keys) {
- const value = Storage.get(key)
- if (value === undefined) {
- Storage.set(key, settings[key])
- }
- }
- },
- }
- function initSettings(userSettings) {
- // Create and inject styles
- const injectStyles = () => {
- const styleEl = document.createElement('style')
- styleEl.textContent = `
- .fixed-container {
- position: fixed;
- z-index: 100;
- width: calc(100vw - 50px);
- max-width: 380px;
- background-color: rgba(255, 255, 255, 0.8);
- backdrop-filter: blur(8px);
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- border-radius: 12px;
- box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
- padding: 30px;
- text-align: center;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
- box-sizing: border-box;
- display: none;
- }
- [data-theme="dark"] .fixed-container {
- background-color: rgba(30, 30, 30, 0.8);
- color: #fff;
- }
- .container-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
- }
- .dropdown-select {
- padding: 8px;
- padding-right:16px;
- border-radius: 6px;
- border: 1px solid #e2e2e2;
- background-color: #f5f5f5;
- font-size: 14px;
- width: 100%;
- }
- [data-theme="dark"] .dropdown-select {
- background-color: #333;
- border-color: #555;
- color: #fff;
- }
- .checkbox-container {
- display: flex;
- align-items: center;
- margin-bottom: 16x;
- text-align: left;
- font-size:14px;
- }
- .input-group {
- display: flex;
- align-items: center;
- margin-bottom: 16px;
- justify-content:flex-start;
- }
- .input-group label {
- text-align: left;
- font-size: 14px;
- margin-right:8px;
- }
- .input-group input {
- max-width: 40px;
- padding: 6px;
- border-radius: 6px;
- border: 1px solid #e2e2e2;
- text-align: center;
- }
- [data-theme="dark"] .input-group input {
- background-color: #333;
- border-color: #555;
- color: #fff;
- }
- .button-group {
- display: flex;
- justify-content: space-between;
- gap: 12px;
- }
- .button-group button {
- flex: 1;
- padding: 10px;
- border-radius: 6px;
- border: none;
- font-size: 16px;
- cursor: pointer;
- }
- .cancel-btn {
- background-color: white;
- border: 1px solid #e2e2e2;
- }
- [data-theme="dark"] .cancel-btn {
- background-color: #333;
- border-color: #555;
- color: #fff;
- }
- .save-btn {
- background-color: #333;
- color: white;
- }
- [data-theme="dark"] .save-btn {
- background-color: #555;
- }
- button:hover{
- filter: brightness(1.5);
- transition: all 0.3s;
- }
- strong svg{
- max-width:21px;
- max-height:21px;
- transform: translateY(2px);
- margin-right: 10px;
- }
- [data-theme="dark"] strong svg{
- filter: invert(1);
- }
- input[type="checkbox"]{
- width:20px;
- height:20px;
- margin:0;
- cursor:pointer;
- }
- .checkbox-container input[type="checkbox"] {
- margin-right: 12px;
- transform: translateY(1.5px);
- }
- `
- document.head.append(styleEl)
- }
- // Create DOM elements and construct the UI
- const createSettingsDialog = () => {
- // Create container
- const container = document.createElement('div')
- container.className = 'fixed-container'
- // Create header with dropdown
- const header = document.createElement('div')
- header.className = 'container-header'
- const spacerLeft = document.createElement('div')
- spacerLeft.style.width = '24px'
- const dropdown = document.createElement('select')
- dropdown.className = 'dropdown-select'
- const optionHot = document.createElement('option')
- optionHot.value = 'reactionCount'
- optionHot.textContent = '按热度(贴贴数)排序'
- const optionReply = document.createElement('option')
- optionReply.value = 'replyCount'
- optionReply.textContent = '按评论数排序'
- const optionRecent = document.createElement('option')
- optionRecent.value = 'newFirst'
- optionRecent.textContent = '按时间排序(最新在前)'
- const optionOld = document.createElement('option')
- optionOld.value = 'oldFirst'
- optionOld.textContent = '按时间排序(最旧在前)'
- dropdown.append(optionHot)
- dropdown.append(optionRecent)
- dropdown.append(optionOld)
- dropdown.append(optionReply)
- dropdown.value = userSettings.sortMode || 'reactionCount'
- const spacerRight = document.createElement('div')
- spacerRight.style.width = '24px'
- header.append($('<strong></strong>').html(Icons.sorting)[0])
- header.append(dropdown)
- header.append(spacerRight)
- // Create checkbox
- const checkboxContainer = document.createElement('div')
- checkboxContainer.className = 'checkbox-container'
- const checkbox = document.createElement('input')
- checkbox.type = 'checkbox'
- checkbox.id = 'showMine'
- checkbox.checked = userSettings.stickyMentioned || false
- const checkboxLabel = document.createElement('label')
- checkboxLabel.htmlFor = 'showMine'
- checkboxLabel.textContent = '置顶我发表/回复我的帖子'
- checkboxContainer.append(checkbox)
- checkboxContainer.append(checkboxLabel)
- // Create min effective number input
- const minEffGroup = document.createElement('div')
- minEffGroup.className = 'input-group'
- const minEffLabel = document.createElement('label')
- minEffLabel.htmlFor = 'minEffectiveNumber'
- minEffLabel.textContent = '最低有效字数 (>=0)'
- const minEffInput = document.createElement('input')
- minEffInput.type = 'number'
- minEffInput.id = 'minEffectiveNumber'
- minEffInput.value = userSettings.minimumFeaturedCommentLength || 0
- minEffGroup.append($('<strong></strong>').html(Icons.font)[0])
- minEffGroup.append(minEffLabel)
- minEffGroup.append(minEffInput)
- // Create max selected posts input
- const maxPostsGroup = document.createElement('div')
- maxPostsGroup.className = 'input-group'
- const maxPostsLabel = document.createElement('label')
- maxPostsLabel.htmlFor = 'maxSelectedPosts'
- maxPostsLabel.textContent = '最大精选评论数 (>0)'
- const maxPostsInput = document.createElement('input')
- maxPostsInput.type = 'number'
- maxPostsInput.id = 'maxSelectedPosts'
- maxPostsInput.value = userSettings.maxFeaturedComments || 1
- maxPostsGroup.append($('<strong></strong>').html(Icons.answerSheet)[0])
- maxPostsGroup.append(maxPostsLabel)
- maxPostsGroup.append(maxPostsInput)
- const spaceHr = document.createElement('hr')
- spaceHr.style.marginBottom = '16px'
- spaceHr.style.border = 'none'
- // Create buttons
- const buttonGroup = document.createElement('div')
- buttonGroup.className = 'button-group'
- const cancelBtn = document.createElement('button')
- cancelBtn.className = 'cancel-btn'
- cancelBtn.textContent = '取消'
- const saveBtn = document.createElement('button')
- saveBtn.className = 'save-btn'
- saveBtn.textContent = '保存'
- buttonGroup.append(cancelBtn)
- buttonGroup.append(saveBtn)
- // Assemble everything
- container.append(header)
- container.append(minEffGroup)
- container.append(maxPostsGroup)
- container.append(checkboxContainer)
- container.append(spaceHr)
- container.append(buttonGroup)
- // Add to document
- document.body.append(container)
- return {
- container,
- dropdown,
- checkbox,
- minEffInput,
- maxPostsInput,
- cancelBtn,
- saveBtn,
- }
- }
- // Initialize settings from localStorage
- const initSettings = (elements) => {
- const { dropdown, checkbox, minEffInput, maxPostsInput } = elements
- if (localStorage.getItem('sortBy')) {
- dropdown.value = localStorage.getItem('sortBy')
- }
- if (localStorage.getItem('showMine') !== null) {
- checkbox.checked = localStorage.getItem('showMine') === 'true'
- }
- if (localStorage.getItem('minEffectiveNumber')) {
- minEffInput.value = localStorage.getItem('minEffectiveNumber')
- }
- if (localStorage.getItem('maxSelectedPosts')) {
- maxPostsInput.value = localStorage.getItem('maxSelectedPosts')
- }
- }
- // Save settings
- const saveSettings = (elements) => {
- const { container, dropdown, checkbox, minEffInput, maxPostsInput } = elements
- Storage.set(
- 'minimumFeaturedCommentLength',
- Math.max(Number.parseInt(minEffInput.value) || 0, 0),
- )
- Storage.set(
- 'maxFeaturedComments',
- Number.parseInt(maxPostsInput.value) > 0 ? Number.parseInt(maxPostsInput.value) : 1,
- )
- // Storage.set('hidePlainComments', setHidePlainCommentsInput.is(':checked'))
- Storage.set('stickyMentioned', checkbox.checked)
- Storage.set('sortMode', dropdown.value)
- // Trigger custom event
- const event = new CustomEvent('settingsSaved', {
- detail: {
- sortBy: dropdown.value,
- showMine: checkbox.checked,
- minEffectiveNumber: Number.parseInt(minEffInput.value),
- maxSelectedPosts: Number.parseInt(maxPostsInput.value),
- },
- })
- document.dispatchEvent(event)
- // jQuery compatibility
- if (window.jQuery) {
- jQuery(document).trigger('settingsSaved', {
- sortBy: dropdown.value,
- showMine: checkbox.checked,
- minEffectiveNumber: Number.parseInt(minEffInput.value),
- maxSelectedPosts: Number.parseInt(maxPostsInput.value),
- })
- }
- hideDialog(container)
- }
- // Show dialog
- const showDialog = (container) => {
- container.style.display = 'block'
- }
- // Hide dialog
- const hideDialog = (container) => {
- container.style.display = 'none'
- }
- // Main initialization function
- const init = () => {
- // Inject CSS
- injectStyles()
- // Create the dialog
- const elements = createSettingsDialog()
- // Initialize settings
- initSettings(elements)
- // Setup event listeners
- elements.saveBtn.addEventListener('click', () => saveSettings(elements))
- elements.cancelBtn.addEventListener('click', () => hideDialog(elements.container))
- // Expose API
- window.settingsDialog = {
- show: () => showDialog(elements.container),
- hide: () => hideDialog(elements.container),
- save: () => saveSettings(elements),
- getElements: () => elements,
- }
- }
- // Auto-initialize when DOM is ready
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', init)
- } else {
- init()
- }
- }
- const BGM_EP_REGEX = /^https:\/\/(((fast\.)?bgm\.tv)|(chii\.in)|(bangumi\.tv))\/ep\/\d+/
- const BGM_GROUP_REGEX =
- /^https:\/\/(((fast\.)?bgm\.tv)|(chii\.in)|(bangumi\.tv))\/group\/topic\/\d+/
- // quickSort is not strictly needed cause JavaScript has built-in sort method based on quicksort/selection algorithm
- function quickSort(arr, sortKey, changeCompareDirection = false) {
- if (arr.length <= 1) {
- return arr
- }
- const pivot = arr[0]
- const left = []
- const right = []
- for (let i = 1; i < arr.length; i++) {
- const element = arr[i]
- const elementImportant = element.important || false
- const pivotImportant = pivot.important || false
- let compareR###lt
- if (elementImportant !== pivotImportant) {
- compareR###lt = elementImportant // true if element is important and pivot is not
- } else if (changeCompareDirection) {
- compareR###lt = element[sortKey] < pivot[sortKey]
- } else {
- compareR###lt = element[sortKey] > pivot[sortKey]
- }
- if (compareR###lt) {
- left.push(element)
- } else {
- right.push(element)
- }
- }
- return quickSort(left, sortKey, changeCompareDirection).concat(
- pivot,
- quickSort(right, sortKey, changeCompareDirection),
- )
- }
- function purifiedDatetimeInMillionSeconds(timestamp) {
- return new Date(timestamp.trim().replace('- ', '')).getTime()
- }
- function processComments(userSettings) {
- // check if the target element is valid
- const username = $('.idBadgerNeue .avatar').attr('href')
- ? $('.idBadgerNeue .avatar').attr('href').split('/user/')[1]
- : ''
- const conservedPostID =
- $(location).attr('href').split('#').length > 1 ? $(location).attr('href').split('#')[1] : null
- const allCommentRows = $('.row.row_reply.clearit')
- let plainCommentsCount = 0
- let featuredCommentsCount = 0
- const minimumContentLength = userSettings.minimumFeaturedCommentLength
- const container = $('#comment_list')
- const plainCommentElements = []
- const featuredCommentElements = []
- let conservedRow = null
- allCommentRows.each(function (index, row) {
- const that = $(this)
- const content = $(row)
- .find(BGM_EP_REGEX.test(location.href) ? '.message.clearit' : '.inner')
- .text()
- let commentScore = 0
- // prioritize @me comments on
- const highlightMentionedColor = '#ff8c00'
- const subReplyContent = that.find('.topic_sub_reply')
- const commentsCount = subReplyContent.find('.sub_reply_bg').length
- const mentionedInMainComment =
- userSettings.stickyMentioned &&
- that.find('.avatar').attr('href').split('/user/')[1] === username
- let mentionedInSubReply = false
- if (mentionedInMainComment) {
- that.css('border-color', highlightMentionedColor)
- that.css('border-width', '1px')
- that.css('border-style', 'dashed')
- commentScore += 10000
- }
- that.find(`.topic_sub_reply .sub_reply_bg.clearit`).each(function (index, element) {
- if (userSettings.stickyMentioned && $(element).attr('data-item-user') === username) {
- $(element).css('border-color', highlightMentionedColor)
- $(element).css('border-width', '1px')
- $(element).css('border-style', 'dashed')
- commentScore += 1000
- mentionedInSubReply = true
- }
- })
- const important = mentionedInMainComment || mentionedInSubReply
- that.find('span.num').each(function (index, element) {
- commentScore += Number.parseInt($(element).text())
- })
- const hasConservedReply = conservedPostID && that.find(`#${conservedPostID}`).length > 0
- if (hasConservedReply) conservedRow = row
- if (!hasConservedReply) subReplyContent.hide()
- const timestampArea = that.find('.action').first()
- if (commentsCount !== 0) {
- const a = $(
- `<a class="expand_all" href="javascript:void(0)" style="margin:0 3px 0 5px;"><span class="ico ico_reply">展开(+${commentsCount})</span></a>`,
- )
- mentionedInSubReply && a.css('color', highlightMentionedColor)
- a.on('click', function () {
- subReplyContent.slideToggle()
- })
- const el = $(`<div class="action"></div>`).append(a)
- timestampArea.after(el)
- }
- // check if this comment meets the requirement of minimumContentLength
- const isShortReply = content.trim().length < minimumContentLength
- let isFeatured =
- userSettings.sortMode === 'reactionCount' ? commentScore >= 1 : commentsCount >= 1
- if (isShortReply || featuredCommentsCount >= userSettings.maxFeaturedComments) {
- isFeatured = false
- }
- // conserved reply must be fixed
- if (hasConservedReply || important) {
- isFeatured = true
- }
- const timestamp = isFeatured
- ? $(row)
- .find('.action:eq(0) small')
- .first()
- .contents()
- .filter(function () {
- return this.nodeType === 3 // Node.TEXT_NODE === 3
- })
- .first()
- .text()
- : $(row).find('small').text().trim()
- if (isFeatured) {
- featuredCommentsCount++
- featuredCommentElements.push({
- element: row,
- score: commentScore,
- commentsCount,
- timestampNumber: purifiedDatetimeInMillionSeconds(timestamp),
- important,
- })
- } else {
- plainCommentsCount++
- plainCommentElements.push({
- element: row,
- score: commentScore,
- timestamp,
- timestampNumber: purifiedDatetimeInMillionSeconds(timestamp),
- })
- }
- })
- return {
- plainCommentsCount,
- featuredCommentsCount,
- container,
- plainCommentElements,
- featuredCommentElements,
- conservedRow,
- }
- }
- ;(async function () {
- if (!BGM_EP_REGEX.test(location.href) && !BGM_GROUP_REGEX.test(location.href)) {
- return
- }
- Storage.init({
- hidePlainComments: true,
- minimumFeaturedCommentLength: 15,
- maxFeaturedComments: 99,
- sortMode: 'reactionCount',
- stickyMentioned: false,
- })
- const userSettings = {
- hidePlainComments: Storage.get('hidePlainComments'),
- minimumFeaturedCommentLength: Storage.get('minimumFeaturedCommentLength'),
- maxFeaturedComments: Storage.get('maxFeaturedComments'),
- sortMode: Storage.get('sortMode'),
- stickyMentioned: Storage.get('stickyMentioned'),
- }
- const sortModeData = userSettings.sortMode || 'reactionCount'
- /**
- * Main
- */
- let {
- plainCommentsCount,
- featuredCommentsCount,
- container,
- plainCommentElements,
- featuredCommentElements,
- conservedRow,
- } = processComments(userSettings)
- let stateBar = container.find('.row_state.clearit')
- if (stateBar.length === 0) {
- stateBar = $(`<div id class="row_state clearit"></div>`)
- }
- const hiddenCommentsInfo = $(
- `<div class="filtered" id="toggleFilteredBtn" style="cursor:pointer;color:#48a2c3;">点击展开/折叠剩余${plainCommentsCount}条普通评论</div>`,
- ).click(function () {
- $('#comment_list_plain').slideToggle()
- })
- stateBar.append(hiddenCommentsInfo)
- container.find('.row').detach()
- const settingBtn = $('<strong></strong>')
- .css({
- display: 'inline-block',
- width: '20px',
- height: '20px',
- transform: 'translate(4px, -3px)',
- cursor: 'pointer',
- })
- .html(Icons.gear)
- .click(() => window.settingsDialog.show())
- container.append(
- $(
- '<h3 style="padding:10px;display:flex;width:100%;align-items:center;">所有精选评论</h3>',
- ).append(settingBtn),
- )
- const trinity = {
- reactionCount() {
- featuredCommentElements = quickSort(featuredCommentElements, 'score')
- },
- replyCount() {
- featuredCommentElements = quickSort(featuredCommentElements, 'commentsCount')
- },
- oldFirst() {
- featuredCommentElements = quickSort(featuredCommentElements, 'timestampNumber', true)
- },
- newFirst() {
- featuredCommentElements = quickSort(featuredCommentElements, 'timestampNumber')
- },
- }
- trinity[sortModeData]()
- /**
- * Append components
- */
- featuredCommentElements.forEach(function (element) {
- container.append($(element.element))
- })
- plainCommentElements.forEach(function (element) {
- container.append($(element.element))
- })
- container.append(stateBar)
- const plainCommentsContainer = $('<div id="comment_list_plain" style="margin-top:2rem;"></div>')
- if (userSettings.hidePlainComments) {
- plainCommentsContainer.hide()
- }
- plainCommentElements.forEach(function (element) {
- plainCommentsContainer.append($(element.element))
- })
- container.append(plainCommentsContainer)
- // Scroll to conserved row if exists
- if (conservedRow) {
- $('html, body').animate(
- {
- scrollTop: $(conservedRow).offset().top,
- },
- 2000,
- )
- }
- $('#sortMethodSelect').val(sortModeData)
- if (featuredCommentsCount < 10 && userSettings.hidePlainComments === true) {
- $('#toggleFilteredBtn').click()
- }
- initSettings(userSettings)
- // control center
- $(document).on('settingsSaved', (event, data) => {
- location.reload()
- })
- })()