NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name 河畔新旧版通用水印处理
// @namespace https://openuserjs.org/users/Northie
// @version 2026.08.26
// @description 新版河畔替换 SVG 水印 UID,旧版河畔隐藏 Canvas 生成的 PNG 水印
// @author Northie
// @copyright 2026, Northie (https://openuserjs.org/users/Northie)
// @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @homepageURL https://github.com/WhyPilotXia/Crack-SVG-watermark-for-bbs.uestc.edu.cn
// @supportURL https://github.com/WhyPilotXia/Crack-SVG-watermark-for-bbs.uestc.edu.cn/issues
// @match https://bbs.uestc.edu.cn/*
// @grant none
// @run-at document-start
// ==/UserScript==
// ==OpenUserJS==
// @author Northie
// ==/OpenUserJS==
(function () {
'use strict'
const OLD_TEXT = '9999' // 新版页面当前水印中的 UID,改成你自己的 UID
const NEW_TEXT = '114514' // 新版页面希望显示的 UID
const STYLE_ID = 'tm-qshp-universal-watermark-style'
const SVG_BASE64_PATTERN = /data:image\/svg\+xml(?:;charset=[^;,]+)?;base64,([A-Za-z0-9+/=_-]+)/gi
function decodeBase64Utf8(base64) {
let normalized = base64.replace(/-/g, '+').replace(/_/g, '/')
const remainder = normalized.length % 4
if (remainder) normalized += '='.repeat(4 - remainder)
const binary = atob(normalized)
const bytes = Uint8Array.from(binary, char => char.charCodeAt(0))
return new TextDecoder().decode(bytes)
}
function encodeBase64Utf8(text) {
const bytes = new TextEncoder().encode(text)
let binary = ''
for (let index = 0; index < bytes.length; index += 0x8000) {
binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000))
}
return btoa(binary)
}
function rewriteBackgroundImage(style) {
if (!style || !OLD_TEXT || OLD_TEXT === NEW_TEXT) return false
const backgroundImage = style.backgroundImage
if (!backgroundImage || !backgroundImage.includes('data:image/svg+xml')) return false
let rewritten = false
const nextBackgroundImage = backgroundImage.replace(
SVG_BASE64_PATTERN,
(dataUrl, base64) => {
try {
const svg = decodeBase64Utf8(base64)
if (!svg.includes(OLD_TEXT)) return dataUrl
const modifiedSvg = svg.replaceAll(OLD_TEXT, NEW_TEXT)
rewritten = true
return dataUrl.slice(0, -base64.length) + encodeBase64Utf8(modifiedSvg)
} catch (error) {
console.warn('[QSHp watermark] SVG 水印解析失败', error)
return dataUrl
}
}
)
if (!rewritten) return false
style.backgroundImage = nextBackgroundImage
console.info(`[QSHp watermark] 新版 SVG 水印已替换:${OLD_TEXT} → ${NEW_TEXT}`)
return true
}
function processElement(element) {
if (!(element instanceof Element)) return
rewriteBackgroundImage(element.style)
}
function processNode(node) {
if (!node) return
if (node.nodeType === Node.ELEMENT_NODE) {
processElement(node)
}
if (
node.nodeType === Node.ELEMENT_NODE ||
node.nodeType === Node.DOCUMENT_FRAGMENT_NODE
) {
node
.querySelectorAll?.('[style*="data:image/svg+xml"]')
.forEach(processElement)
}
}
function installInsertionHooks() {
const rawAppendChild = Node.prototype.appendChild
const rawInsertBefore = Node.prototype.insertBefore
const rawReplaceChild = Node.prototype.replaceChild
Node.prototype.appendChild = function (node) {
processNode(node)
return rawAppendChild.call(this, node)
}
Node.prototype.insertBefore = function (node, referenceNode) {
processNode(node)
return rawInsertBefore.call(this, node, referenceNode)
}
Node.prototype.replaceChild = function (newChild, oldChild) {
processNode(newChild)
return rawReplaceChild.call(this, newChild, oldChild)
}
for (const methodName of ['append', 'prepend', 'replaceChildren']) {
const rawMethod = Element.prototype[methodName]
if (typeof rawMethod !== 'function') continue
Element.prototype[methodName] = function (...nodes) {
nodes.forEach(node => {
if (typeof node !== 'string') processNode(node)
})
return rawMethod.apply(this, nodes)
}
}
const rawInsertAdjacentElement = Element.prototype.insertAdjacentElement
if (typeof rawInsertAdjacentElement === 'function') {
Element.prototype.insertAdjacentElement = function (position, element) {
processNode(element)
return rawInsertAdjacentElement.call(this, position, element)
}
}
}
function installLegacyPngRule() {
if (!document.documentElement || document.getElementById(STYLE_ID)) return
const style = document.createElement('style')
style.id = STYLE_ID
style.textContent = `
body > div[style*="data:image/png;base64"][style*="pointer-events"][style*="position"][style*="repeat"] {
display: none !important;
background: none !important;
}
`
;(document.head || document.documentElement).appendChild(style)
}
function initializeDocumentHandling() {
if (!document.documentElement) return false
installLegacyPngRule()
processNode(document.documentElement)
const observer = new MutationObserver(records => {
installLegacyPngRule()
for (const record of records) {
record.addedNodes.forEach(processNode)
}
})
observer.observe(document.documentElement, {
childList: true,
subtree: true
})
return true
}
installInsertionHooks()
if (!initializeDocumentHandling()) {
const bootstrapObserver = new MutationObserver(() => {
if (!initializeDocumentHandling()) return
bootstrapObserver.disconnect()
})
bootstrapObserver.observe(document, { childList: true })
}
})()