// ==UserScript== // @name SPXXKLP // @description Minecraft.net & X.com blog article to BBCode converter, adapted to KLPBBS // @namespace npmjs.com/package/@spxxklp/userscript // @author Cinder & SPGoding & SPX Fellow // @connect feedback.minecraft.com // @connect help.minecraft.net // @connect raw.githubusercontent.com // @connect * // @homepage https://github.com/cinder0601/SPXXKLP // @match https://www.minecraft.net/en-us/article/* // @match https://www.minecraft.net/zh-hans/article/* // @match https://x.com/*/status/* // @match https://feedback.minecraft.net/hc/en-us/articles/* // @match https://help.minecraft.net/hc/en-us/articles/* // @require https://fastly.jsdelivr.net/gh/sizzlemctwizzle/GM_config@2207c5c1322ebb56e401f03c2e581719f909762a/gm_config.js // @version 3.1.3 // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license MIT // @downloadURL none // ==/UserScript== (function () { 'use strict'; GM_config.init({ id: 'spxxklp', title: 'SPXXKLP 用户脚本', fields: { translator: { label: '译者名', type: 'text', default: '<默认译者>' }, bugSource: { label: '选择翻译源', type: 'select', options: ['Github', 'Custom'], default: 'Github' }, bugCenterTranslation: { label: '漏洞翻译源', type: 'text', default: 'https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/crowdin/zh-CN/zh_CN.json' }, bugCenterTranslator: { label: '漏洞译者源', type: 'text', default: 'https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/translator.json' }, bugCenterColor: { label: '漏洞颜色源', type: 'text', default: 'https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/color.json' } } }); GM_registerMenuCommand('Edit Configuration', () => GM_config.open()); const src = GM_config.get('bugSource'); let tr = ""; let tor = ""; let c = ""; if (src == "Github") { console.log("[SPXXKLP] Using Github bug center"); tr = 'https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/crowdin/zh-CN/zh_CN.json'; tor = 'https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/translator.json'; c = 'https://raw.githubusercontent.com/SPXFellow/spxx-translation-database/master/color.json'; }else if (src == "Custom") { console.log("[SPXXKLP] Using custom bug center"); tr = GM_config.get('bugCenterTranslation'); tor = GM_config.get('bugCenterTranslator'); c = GM_config.get('bugCenterColor'); } const config = { translator: GM_config.get('translator'), bugCenter: { translation: tr, translator: tor, color: c } }; var version = "3.1.3"; function getVersionType(url) { const lowerUrl = url.toLowerCase(); if (lowerUrl.includes('snapshot')) { return VersionType.Snapshot; } else if (lowerUrl.includes('pre-release')) { return VersionType.PreRelease; } else if (lowerUrl.includes('release-candidate')) { return VersionType.ReleaseCandidate; } else if (lowerUrl.includes('minecraft-java-edition') && !lowerUrl.includes('snapshot')) { return VersionType.Release; } else if (lowerUrl.includes('minecraft-preview') || lowerUrl.includes('minecraft-beta-preview') || lowerUrl.includes('minecraft-beta')) { return VersionType.BedrockBeta; } else if (lowerUrl.includes('bedrock')) { return VersionType.BedrockRelease; } else { return VersionType.Normal; } } const bugsCenter = config.bugCenter.translation; const bugsTranslatorsTable = config.bugCenter.translator; const translatorColorTable = config.bugCenter.color; const spxxklpVersion = version; const url1 = window.location.href; function getVersionCode(url){ const lowerUrl = url.toLowerCase(); if (lowerUrl.includes('snapshot')) { const versionRegex = /-([0-9a-zA-Z]+)$/; const match = url.match(versionRegex); if (match && match[1]) { return match[1]; } } else if (lowerUrl.includes('pre-release')) { const versionRegex = /(\d+)\-(\d+)\-pre-release\-(\d+)/; const match = lowerUrl.match(versionRegex); if (match && match.length >= 4) { const majorVersion = match[1]; const minorVersion = match[2]; const preReleaseNumber = match[3]; const formattedVersion = `${majorVersion}.${minorVersion}-pre${preReleaseNumber}`; return formattedVersion; } } else if (lowerUrl.includes('release-candidate')) { const versionRegex = /(\d+)\-(\d+)\-release-candidate\-(\d+)/; const match = lowerUrl.match(versionRegex); if (match && match.length >= 4) { const majorVersion = match[1]; const minorVersion = match[2]; const preReleaseNumber = match[3]; const formattedVersion = `${majorVersion}.${minorVersion}-rc${preReleaseNumber}`; return formattedVersion; } } else if (lowerUrl.includes('minecraft-beta-preview')) { const versionRegex = /\-beta-preview\-(\d+)\-(\d+)\-(\d+)\-(\d+)/; const match = lowerUrl.match(versionRegex); if (match && match.length >= 5) { const Version1 = match[1]; const Version2 = match[2]; const Version3 = match[3]; const Version4 = match[4]; const formattedVersion = `${Version1}.${Version2}.${Version3}.${Version4}`; return formattedVersion; } } else if (lowerUrl.includes('minecraft-preview') && !lowerUrl.includes('beta')) { const versionRegex = /\-preview\-(\d+)\-(\d+)\-(\d+)\-(\d+)/; const match = lowerUrl.match(versionRegex); if (match && match.length >= 5) { const Version1 = match[1]; const Version2 = match[2]; const Version3 = match[3]; const Version4 = match[4]; const formattedVersion = `${Version1}.${Version2}.${Version3}.${Version4}`; return formattedVersion; } } else if (lowerUrl.includes('minecraft-beta') && !lowerUrl.includes('preview')) { const versionRegex = /\-beta\-(\d+)\-(\d+)\-(\d+)\-(\d+)/; const match = lowerUrl.match(versionRegex); if (match && match.length >= 5) { const Version1 = match[1]; const Version2 = match[2]; const Version3 = match[3]; const Version4 = match[4]; const formattedVersion = `${Version1}.${Version2}.${Version3}.${Version4}`; return formattedVersion; } } } let versioncode = getVersionCode(url1); function getHeader(articleType, type) { if (articleType.toLowerCase() !== 'news') { return `[align=left][color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][/align][/align][/align][/align][hr]\n`; } switch (type) { case VersionType.Snapshot: return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size] [color=#388e3c][size=5]|[/size][/color][size=4][b]每周快照[/b]是 Minecraft Java 版的测试机制,主要用于下一个正式版的特性预览。[/size] [color=#f44336][size=5]|[/size][/color][size=4]然而,[b]每周快照[/b]主要用于新特性展示,通常存在大量漏洞。因此对于普通玩家建议仅做[color=Red][b]测试尝鲜[/b][/color]用。在快照中打开存档前请务必[color=Red][b]进行备份[/b][/color]。[b]适用于正式版的 Mod 不兼容快照,且大多数 Mod 都不对每周快照提供支持[/b]。 [/size] [color=#f44336][size=5]|[/size][/color][size=4]Minecraft Java 版 <正式版版本号> 仍未发布,${versioncode} 为其第 <计数> 个预览版。[/size] [color=#388e3c][size=5]|[/size][/font][/color][size=4]本文内容按照 [/font][/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][hr]\n`; case VersionType.PreRelease: return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size] [color=#388e3c][size=5]|[/size][/color][size=4][b]预发布版[/b]是 Minecraft Java 版的测试机制,如果该版本作为正式版发布,那么预发布版的游戏文件将与启动器推送的正式版完全相同。[/size] [color=#f44336][size=5]|[/size][/color][size=4]然而,预发布版主要用于服主和 Mod 制作者的预先体验,如果发现重大漏洞,该预发布版会被新的预发布版代替。因此建议普通玩家[color=Red]持观望态度[/color]。 [/size] [color=#f44336][size=5]|[/size][/color][size=4]Minecraft Java 版 <正式版版本号> 仍未发布,${versioncode} 为其第 <计数> 个预览版。[/size] [color=#388e3c][size=5]|[/size][/font][/color][size=4]本文内容按照 [/font][/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][hr]\n`; case VersionType.ReleaseCandidate: return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size] [color=#388e3c][size=5]|[/size][/color][size=4][b]候选版[/b]是Minecraft Java版正式版的候选版本,如果发现重大漏洞,该候选版会被新的候选版代替。如果一切正常,该版本将会作为正式版发布。[/size] [color=#f44336][size=5]|[/size][/color][size=4]候选版已可供普通玩家进行抢鲜体验,但仍需当心可能存在的漏洞。[/size] [color=#f44336][size=5]|[/size][/color][size=4]<正式版版本号> 仍未发布,${versioncode} 为其第 <计数> 个预览版。[/size] [color=#388e3c][size=5]|[/size][/font][/color][size=4]本文内容按照 [/font][/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][hr]\n`; case VersionType.Release: return `[color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft Java 版[/b]是指 Windows、Mac OS 与 Linux 平台上,使用 Java 语言开发的 Minecraft 版本。[/size] [color=#f44336][size=5]|[/size][/color][size=4][b]正式版[/b]是 Minecraft Java 版经过一段时间的预览版测试后得到的稳定版本,也是众多纹理、Mod 与服务器插件会逐渐跟进的版本。官方启动器也会第一时间进行推送。 [/size] [color=#f44336][size=5]|[/size][/color][size=4]建议玩家与服主关注其相关服务端、Mod 与插件的更新,迎接新的正式版吧!专注于单人原版游戏的玩家可立即更新,多人游戏玩家请关注您所在服务器的通知。[/size] [color=#388e3c][size=5]|[/size][/font][/color][size=4]本文内容按照 [/font][/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][hr]\n`; case VersionType.BedrockRelease: return `[align=left][color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft 基岩版[/b]是指运行在移动平台(Android、iOS)、Windows 10、主机(Xbox One、Switch、PlayStation 4)上,使用「基岩引擎」(C++语言)开发的 Minecraft 版本。[/size][/align][/font][align=left][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][align=left][color=#f44336][size=5]|[/size][/color][size=4][b]正式版[/b]是 Minecraft 基岩版经过一段时间的测试版测试之后得到的稳定版本,也是众多纹理、Addon 和官方领域服会逐渐跟进的版本。与此同时 Google Play、Win10 Store 等官方软件商店也会推送此次更新。 [/size][/align][/font][align=left][font=-apple-system, BlinkMacSystemFont,Segoe UI, Roboto, Helvetica, Arial, sans-serif][color=#388e3c][size=5]|[/size][/color][size=4]本文内容按照 [/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/size][/font][hr]\n`; case VersionType.BedrockBeta: return `[align=left][color=#388e3c][size=5]|[/size][/color][size=4][b]Minecraft 基岩版[/b]是指运行在移动平台(Android、iOS)、Windows 10、主机(Xbox One、Switch、PlayStation 4)上,使用「基岩引擎」(C++语言)开发的 Minecraft 版本。[/size][/align][align=left][color=#388e3c][size=5]|[/size][/color][size=4][b]测试版[/b]是 Minecraft 基岩版的测试机制,主要用于下一个正式版的特性预览。[/size][/align][/align][align=center][align=left][color=#f44336][size=5]|[/size][/color][size=4][b]然而,测试版主要用于新特性展示,通常存在大量漏洞。因此对于普通玩家建议仅做测试尝鲜用。使用测试版打开存档前请务必备份。适用于正式版的领域服务器与测试版不兼容。[/b] [/size][/align][/align][align=center][align=left][color=#f44336][size=5]|[/size][/color][size=4]如果在测试版中遇到旧版存档无法使用的问题,测试版将允许你将存档上传以供开发团队查找问题。[/size][/align][/align][align=center][align=left][color=#f44336][size=5]|[/size][/color][size=4]Minecraft 基岩版 <正式版版本号> 仍未发布,Beta & Preview ${versioncode} 为其第 <计数> 个测试版。[/size][/align][/align][align=center][align=left][color=#388e3c][size=5]|[/size][/font][/color][size=4]本文内容按照 [/font][/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][/align][/align][/align][/align][hr]\n`; case VersionType.Normal: default: return `[align=left][color=#388e3c][size=5]|[/size][/font][/color][size=4]本文内容按照 [/font][/size][url=https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans][size=4][color=#2e8b57][u]CC BY-NC-SA 4.0[/u][/color][/size][/url][/font][size=4] 协议进行授权,[b]转载本帖时须注明[color=#ff0000]原作者[/color]以及[color=#ff0000]本帖地址[/color][/b]。[/font][/size][/align][/align][/align][/align][hr]\n`; } } function getFooter(articleType, type) { const time = new Date(); function padTime(time) { return time.toString().padStart(2, '0'); } function toHoursAndMinutes(totalMinutes) { const m = Math.abs(totalMinutes); const minutes = m % 60; const hours = Math.floor(m / 60); return `${totalMinutes < 0 ? '+' : '-'}${padTime(hours)}${padTime(minutes)}`; } const poweredBy = `[align=center][size=1][color=Silver]Powered by SPXXKLP ${spxxklpVersion} with love Converted at ${time.getFullYear()}-${padTime(time.getMonth() + 1) // why +1 javascript }-${padTime(time.getDate())} ${padTime(time.getHours())}:${padTime(time.getMinutes())} ${toHoursAndMinutes(time.getTimezoneOffset())}[/color][/size][/align]`; /*Same contents,change if necessary.**/ switch (type) { case VersionType.Snapshot: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; case VersionType.PreRelease: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; case VersionType.ReleaseCandidate: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; case VersionType.Release: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; case VersionType.BedrockRelease: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; case VersionType.BedrockBeta: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; case VersionType.Normal: default: return `\n${poweredBy}\n[hr][color=#388e3c][size=5]|[/size][/color][size=4][b]想了解更多游戏资讯?[/b][/size][list][*][size=3][url=https://klpbbs.com/forum-2-1.html][color=#388e3c][u]苦力怕论坛 - 游戏资讯版块[/u][/color][/url][/size][/list][/indent]`; } } let VersionType; (function (VersionType) { VersionType[VersionType["Snapshot"] = 1] = "Snapshot"; VersionType[VersionType["PreRelease"] = 2] = "PreRelease"; VersionType[VersionType["ReleaseCandidate"] = 3] = "ReleaseCandidate"; VersionType[VersionType["Release"] = 4] = "Release"; VersionType[VersionType["Normal"] = 5] = "Normal"; VersionType[VersionType["BedrockBeta"] = 6] = "BedrockBeta"; VersionType[VersionType["BedrockRelease"] = 7] = "BedrockRelease"; })(VersionType || (VersionType = {})); const translators = { headings: (input, ctx) => { return translator(input, ctx, [// Minecraft.net titles [/Block of the Week: /gi, '本周方块:'], [/Taking Inventory: /gi, '背包盘点:'], [/Around the Block: /gi, '群系漫游:'], [/A Minecraft Java Snapshot/gi, 'Minecraft Java版 快照'], [/A Minecraft Java Pre-Release/gi, 'Minecraft Java版 预发布版'], [/A Minecraft Java Release Candidate/gi, 'Minecraft Java版 候选版本'], // Bedrock Edition titles [/Minecraft Beta (?:-|——) (.*?) \((.*?)\)/gi, 'Minecraft 基岩版 Beta $1($2)'], [/Minecraft Beta & Preview - (.*?)/g, 'Minecraft 基岩版 Beta & Preview $1'], [/Minecraft (?:-|——) (.*?) \(Bedrock\)/gi, 'Minecraft 基岩版 $1'], [/Minecraft (?:-|——) (.*?) \((.*?) Only\)/gi, 'Minecraft 基岩版 $1(仅$2)'], [/Minecraft (?:-|——) (.*?) \((.*?)\)/gi, 'Minecraft 基岩版 $1(仅$2)'], // BE subheadings [/Marketplace/gi, '市场'], [/Data-Driven/gi, '数据驱动'], [/Graphical/gi, '图像'], [/Player/gi, '玩家'], [/Experimental Features/gi, '实验性特性'], [/Mobs/gi, '生物'], [/Features and Bug Fixes/gi, '特性和漏洞修复'], [/ADVANCEMENTS/gi, '进度'], [/Accessibility/gi, '辅助功能'], [/Gameplay/gi, '玩法'], [/Items/gi, '物品'], [/Blocks/gi, '方块'], [/User Interface/gi, '用户界面'], [/Commands/gi, '命令'], [/Known Issues/gi, '已知问题'], [/Character Creator/gi, '角色创建器'], [/Components/gi, '组件'], [/General/gi, '通用'], [/Technical Experimental Updates/gi, '实验性技术性更新'], [/Gametest Framework/gi, 'Gametest 框架'], [/Gametest Framework (experimental)/gi, 'Gametest 框架(实验性)'], // JE subheadings [/Minecraft Snapshot /gi, 'Minecraft 快照 '], [/ Pre-Release /gi, '-pre'], [/ Release Candidate /gi, '-rc'], [/Release Candidate/gi, '候选版本'], [/New Features in ([^\r\n]+)/gi, '$1 的新增特性'], [/Technical changes in ([^\r\n]+)/gi, '$1 的技术性修改'], [/Changes in ([^\r\n]+)/gi, '$1 的修改内容'], [/Fixed bugs in ([^\r\n]+)/gi, '$1 修复的漏洞'], [/STABILITY AND PERFORMANCE/gi, '性能与稳定性'], [/FEATURES AND BUG FIXES/gi, '特性和漏洞修复'],[/LOOT/gi, '战利品'], [/PARITY/gi, '趋同'], [/ADD-ONS AND SCRIPT ENGINE/gi, '附加包和脚本引擎'], [/DRESSING ROOM/gi, '更衣室'], [/Item/gi, '物品'], [/CHANGES/gi, '改动'], [/SOUNDS/gi, '音效'], [/DATA PACK VERSION/gi, '数据包版本'], [/PREDICATES/gi, '谓词'], [/ENTITY/gi, '实体'], [/ENCHANTMENTS/gi, '附魔'], [/TAGS/gi, '标签'], [/TYPE/gi, '类型'], [/MUSIC/gi, '音乐'], [/GAME TIPS/gi, '游戏提示'], [/NEW FEATURE/gi, '新特性'], [/USER INTERFACE/gi, '用户界面'], [/EDITOR/gi, '编辑器'], [/FIXES/gi, '修复'], [/IMPROVEMENTS/gi, '改进'], [/RESOURCE PACK VERSION/gi, '资源包版本'], [/SHADERS/gi, '着色器'], [/PARTICLES/gi, '粒子效果'], [/TOUCH CONTROLS/gi, '触控'], [/TECHNICAL UPDATES/gi, '技术性更新'], [/PROJECTILES/gi, '弹射物'], [/ENTITIES/gi, '实体'], [/FUNCTIONS/gi, '函数']]); }, imgCredits: (input, ctx) => { return translator(input, ctx, [// Creative Commons image credits [/Image credit:/gi, '图片来源:'], [/CC BY-NC-ND/gi, '知识共享 署名-非商业性使用-禁止演绎'], [/CC BY-NC-SA/gi, '知识共享 署名-非商业性使用-相同方式共享'], [/CC BY-NC/gi, '知识共享 署名-非商业性使用'], [/CC BY-ND/gi, '知识共享 署名-禁止演绎'], [/CC BY-SA/gi, '知识共享 署名-相同方式共享'], [/CC BY/gi, '知识共享 署名'], [/Public Domain/gi, '公有领域']]); }, punctuation: (input, ctx) => { return translator(input, ctx, [[/\[i\]/gi, '[font=楷体]'], [/\[\/i\]/g, '[/font]'], ...(ctx.disablePunctuationConverter ? [] : [[/,( |$)/g, ','], [/!( |$)/g, '!'], [/\.\.\.( |$)/g, '…'], [/\.( |$)/g, '。'], [/\?( |$)/g, '?'], [/( |^)-( |$)/g, ' —— ']])], input => { return quoteTreatment(input, [['“', '”', /"/]]); }); }, // eslint-disable-next-line @typescript-eslint/no-unused-vars code: (input, ctx) => { return quoteTreatment(input, [['[backcolor=#f1edec][color=Silver][font=SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace][/font][/color][/backcolor]', '`', /`/]]); } }; function translate(input, ctx, type) { if (typeof type === 'string') { type = [type]; } for (const t of type) { input = translators[t](input, ctx); } return input; } function quoteTreatment(input, quoteArrays) { for (const quoteArray of quoteArrays) { const split = input.split(quoteArray[2]); input = ''; for (let i = 0; i < split.length - 1; i++) { const element = split[i]; input += element + quoteArray[i % 2]; } input += split[split.length - 1]; } return input; } function translator(input, ctx, mappings, treatment = input => input) { // REPLACE!!!!1 for (const mapping of mappings) { input = input.replace(mapping[0], mapping[1]); } treatment(input, ctx); return input; } const converters = { /** * Converts a ChildNode to a BBCode string according to the type of the node. */ convert: async (node, ctx) => { if (node.classList?.contains('spxxklp-userscript-ignored')) { return ''; } // Listing all possible elements in the document switch (node.nodeName) { case 'A': return converters.a(node, ctx); case 'B': case 'STRONG': return converters.strong(node, ctx); case 'BLOCKQUOTE': return converters.blockquote(node, ctx); case 'BR': return converters.br(); case 'CITE': return converters.cite(node, ctx); case 'CODE': return converters.code(node, ctx); case 'DIV': case 'SECTION': return converters.div(node, ctx); case 'DD': return converters.dd(node, ctx); case 'DL': return converters.dl(node, ctx); case 'DT': return converters.dt(); case 'EM': return converters.em(node, ctx); case 'H1': return converters.h1(node, ctx); case 'H2': return converters.h2(node, ctx); case 'H3': return converters.h3(node, ctx); case 'H4': return converters.h4(node, ctx); case 'I': return converters.i(node, ctx); case 'IMG': return converters.img(node); case 'LI': return converters.li(node, ctx); case 'OL': return converters.ol(node, ctx); case 'P': return converters.p(node, ctx); case 'PICTURE': return converters.picture(node, ctx); case 'PRE': return converters.pre(node, ctx); case 'SPAN': return converters.span(node, ctx); case 'TABLE': return converters.table(node, ctx); case 'TBODY': return converters.tbody(node, ctx); case 'TH': case 'TD': return converters.td(node, ctx); case 'TR': return converters.tr(node, ctx); case 'UL': return converters.ul(node, ctx); case '#text': if (node) { if (ctx.multiLineCode) { return node.textContent ? node.textContent : ''; } else return node.textContent.replace(/[\n\r\t]+/g, '').replace(/\s{2,}/g, ''); } else { return ''; } case 'BUTTON': case 'H5': case 'NAV': case 'svg': case 'SCRIPT': if (node) { return node.textContent ? node.textContent : ''; } else { return ''; } default: console.warn(`Unknown type: '${node.nodeName}'.`); if (node) { return node.textContent ? node.textContent : ''; } else { return ''; } } }, /** * Convert child nodes of an HTMLElement to a BBCode string. */ recurse: async (ele, ctx) => { let ans = ''; if (!ele) { return ans; } for (const child of Array.from(ele.childNodes)) { ans += await converters.convert(child, ctx); } return ans; }, a: async (anchor, ctx) => { const url = resolveUrl(anchor.href); let ans; if (url) { ans = `[url=${url}][color=#388d40][u]${await converters.recurse(anchor, ctx)}[/u][/color][/url]`; } else { ans = await converters.recurse(anchor, ctx); } return ans; }, blockquote: async (ele, ctx) => { const prefix = ''; const suffix = ''; const ans = `${prefix}${await converters.recurse(ele, ctx)}${suffix}`; return ans; }, br: async () => { const ans = '\n'; return ans; }, cite: async (ele, ctx) => { const prefix = '—— '; const suffix = ''; const ans = `${prefix}${await converters.recurse(ele, ctx)}${suffix}`; return ans; }, code: async (ele, ctx) => { const prefix = ctx.multiLineCode ? '[code]' : '[backcolor=#f1edec][color=#7824c5][font=SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace]'; const suffix = ctx.multiLineCode ? '[/code]' : '[/font][/color][/backcolor]'; const ans = `${prefix}${await converters.recurse(ele, { ...ctx, disablePunctuationConverter: true })}${suffix}`; return ans; }, div: async (ele, ctx) => { let ans = await converters.recurse(ele, ctx); if (ele.classList.contains('text-center')) { ans = `[/indent][/indent][align=center]${ans}[/align][indent][indent]\n`; } else if (ele.classList.contains('article-image-carousel')) { // Image carousel. /* *