Sleazy Fork is available in English.

HV简称替换为中文

受不了简称了

  1. // ==UserScript==
  2. // @name HV简称替换为中文
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.2
  5. // @description 受不了简称了
  6. // @author aotmd
  7. // @match https://forums.e-hentai.org/index.php?showtopic=*
  8. // @match https://ehwiki.org/wiki/Acronyms/Chinese
  9. // @noframes
  10. // @license MIT
  11. // @run-at document-body
  12. // @grant none
  13. // ==/UserScript==
  14. //简称来源:https://ehwiki.org/wiki/Acronyms/Chinese
  15. /*生成脚本:
  16. let a=document.querySelectorAll("#mw-content-text > div > table > tbody > tr")
  17. let s="";
  18. for ( let i = 0; i < a.length; i++ ) {
  19. s+="\'"+a[i].children[0].innerText+"\'"+":\'"+a[i].children[2].innerText+"\',\n"
  20. }
  21. */
  22. /*
  23. 参见:https://forums.e-hentai.org/index.php?showtopic=189266
  24. 参见:https://forums.e-hentai.org/index.php?showtopic=256084&st=0&p=6092109&#entry6092109
  25. 参见:https://ehwiki.org/wiki/Acronyms/Chinese
  26. */
  27. ( function() {
  28. let map = {
  29. '10B': 'T氏、天妇罗、菠萝、大菠萝',
  30. '1H': '单手武器/战斗风格:单手',
  31. '2H': '双手武器/战斗风格:双手',
  32. 'AAB': '攻击准确度奖励:这边指的是物理性质',
  33. 'AB': '训练:技能推广',
  34. 'ADB': '攻击伤害奖励',
  35. 'AF': '咒语:奥术集成',
  36. 'AGI': '主要属性奖励:敏捷',
  37. 'AL': '训练:善于学习者',
  38. 'AoE': '范围效果',
  39. 'AP': '技能点',
  40. 'Arch': '潜能:大法师',
  41. 'AS': '行动速度',
  42. 'B': '负重',
  43. 'Bur':'负重',
  44. 'BC': '后缀:战法师',
  45. 'BoD': '黏合剂:毁灭',
  46. 'BoP': '黏合剂:守护',
  47. 'BoS': '黏合剂:杀戮',
  48. 'But': '潜能:屠夫',
  49. 'BW': '特效:流血',
  50. 'C': '绅士幣',
  51. 'Cap': '潜能:魔力电容',
  52. 'CM': '特效:魔力合流',
  53. 'CoD': '货到付款',
  54. 'CR': '罕见素材:Crystallized Phazon',
  55. 'DEX': '主要属性奖励:灵巧',
  56. 'DMM': '罕见素材:Defense Matrix Modulator',
  57. 'DNP': '请勿张贴',
  58. 'DOT': '持续伤害',
  59. 'DW': '战斗风格:双持',
  60. 'DwD': '竞技场:与龙共舞/与龙共舞的敌人',
  61. 'DX': '解剔除',
  62. 'Eco': '潜能:节能装置',
  63. 'ED': '能量饮料/有效伤害/竞技场:永恆的黑暗',
  64. 'EDB': '元素伤害奖励',
  65. 'EH': 'E变态',
  66. 'E-H': 'E变态',
  67. 'EHG': 'E变态图库',
  68. 'EHP': '有效生命值',
  69. 'EHT': 'E变态BT伺服器',
  70. 'Ele': '元素类型前缀',
  71. 'Emax': '精致装备上某数值的顶点。',
  72. 'Exmax': '精致装备上某数值的顶点。',
  73. 'END': '主要属性奖励:体质',
  74. 'EoD': '竞技场:世界末日/竞技场:死亡前夕',
  75. 'ET': '特效:以太水龙头',
  76. 'Eth': '前缀:空灵',
  77. 'EW': '后缀:地行者',
  78. 'EXP': '经验值',
  79. 'Exq': '品质:精致',
  80. 'Fat': '潜能:致命性',
  81. 'FB': '技巧:狂乱百裂斩',
  82. 'FoS': '额外能力:雪花的信徒',
  83. 'FRD': '技巧:龙吼',
  84. 'FSM': '敌人:飞行义大利面怪物',
  85. 'GF': '压榨界',
  86. 'GLT': '黄金彩券',
  87. 'GP': '图库点数',
  88. 'GS': '金星',
  89. 'GSC': '金星俱乐部',
  90. 'H': 'Hath',
  91. 'H@H': 'Hentai@Home',
  92. 'Hellfest': '地狱榨:地狱难度的压榨界',
  93. 'HG': '高阶素材',
  94. 'HGC': '高阶布料素材',
  95. 'HGL': '高阶皮革素材',
  96. 'HGM': '高阶金属素材',
  97. 'HGW': '高阶木材素材',
  98. 'HP': '生命值',
  99. 'HS': '咒语:穿心',
  100. 'HV': '变态之道:呼应我要成为大变态',
  101. 'Hverse': '变态之道:呼应我要成为大变态',
  102. 'I': '干扰',
  103. 'Int':'干扰',
  104. 'IA': '额外能力:天赋奥术',
  105. 'INT': '主要属性奖励:智力',
  106. 'IPU': '敌人:隐形粉红独角兽',
  107. 'ISB': '道具店机器人',
  108. 'IW': '道具界',
  109. 'IWBTH': '难易度:我要成为大变态',
  110. 'Jug': '潜能:重装盔甲',
  111. 'LE': '终极万能药',
  112. 'Leg': '品质:传奇',
  113. 'LG': '低阶素材',
  114. 'Lmax': '传奇装备上某数值的顶点。',
  115. 'LotD': '训练:抽签运',
  116. 'MAB': '魔法准确度奖励',
  117. 'Mag': '品质:华丽',
  118. 'Mat': '素材',
  119. 'MDB': '魔法伤害奖励',
  120. 'Mit': '伤缓',
  121. 'MG': '中阶素材',
  122. 'ML': '怪物实验室',
  123. 'MM': '莫古利邮务',
  124. 'Mmax': '华丽装备上某数值的顶点。',
  125. 'MMI': '魔法缓伤:缓伤意思就像缓冲,让伤害值踩煞车,所以此数值无法达到 100%',
  126. 'MMit': '魔法缓伤:缓伤意思就像缓冲,让伤害值踩煞车,所以此数值无法达到 100%',
  127. 'MP': '魔力值',
  128. 'MPB': '魔法熟练度奖励',
  129. 'MPV': '多页阅读器',
  130. 'NS': '命名空间',
  131. 'OC': '斗气:以前翻作怒气,但西方人应该比较熟悉斗气吧',
  132. 'OFC': '技巧:友情小马砲',
  133. 'PA': '特效:破甲',
  134. 'PAB': '主要属性奖励',
  135. 'PB': '回本',
  136. 'RE': '随机遭遇',
  137. 'Pen': '潜能:渗透',
  138. 'PFUDOR': '难易度:粉红毛毛独角兽在彩虹上头舞动',
  139. 'PL': '战斗力',
  140. 'PM': '私人讯息',
  141. 'PMI': '物理缓伤',
  142. 'PMit':'物理缓伤',
  143. 'PXP': '潜经验:难得中文也能取字首,自认为念起来还颇顺口的',
  144. 'QM': '训练:军需官',
  145. 'RA': '罕见素材:Repurposed Actuator',
  146. 'RL': '敌人:现实生活',
  147. 'RNG': '随机数发生器',
  148. 'RoB': '浴血擂台',
  149. 'ROI': '投资报酬率',
  150. 'RP': '角色扮演 (论坛,避免使用)/真人色情 (在剔除日志里用简写)',
  151. 'SD': '后缀:影舞者',
  152. 'SF': '罕见素材:Shade Fragment',
  153. 'SG': '敌人:女高中生',
  154. 'Smax': '优秀装备上某数值的顶点。',
  155. 'SoL': '咒语:生命火花',
  156. 'SP': '灵力值',
  157. 'SS': '灵动架式/咒语:灵力盾',
  158. 'STR': '主要属性奖励:力量',
  159. 'SV': '咒语:影纱',
  160. 't/s': '每秒刷数',
  161. 'TpS': '每秒刷数',
  162. 'T&T': '竞技场:命运三女神与树/命运三女神与树的敌人',
  163. 'TT': '竞技场:命运三女神与树/命运三女神与树的敌人',
  164. 'ToB': '血令牌',
  165. 'ToS': '服务条款',
  166. 'TT&T': '浴血擂台:九死一树',
  167. 'TTT': '浴血擂台:九死一树',
  168. 'WIS': '主要属性奖励:感知',
  169. 'WTB': '愿意购买',
  170. 'WTS': '愿意卖出',
  171. 'WTT': '愿意交换',
  172. /*自定义部分:*/
  173. 'CS':'施法速度',
  174. 'Prof':'熟练度加成',
  175. 'PF':'熟练度系数',
  176. 'Evade':'回避',
  177. 'Resist':'抵抗',
  178.  
  179. }
  180. const 递归 = ( function 闭包() {
  181. /**
  182. * 递归处理到每个节点
  183. * @param el 要处理的节点
  184. * @param func 调用的函数
  185. */
  186. function 主控( el, func ) {
  187. const nodeList = el.childNodes;
  188. /*先处理自己*/
  189. 统一处理( el, false, func );
  190. for ( let i = 0; i < nodeList.length; i++ ) {
  191. const node = nodeList[ i ];
  192. 统一处理( node, true, func );
  193. }
  194. }
  195.  
  196. /**
  197. * 捕获指定节点,属性和属性内容,并执行指定函数.
  198. * @param el 要处理的节点
  199. * @param recursion 是否递归
  200. * @param func 调用的函数
  201. */
  202. function 统一处理( el, recursion, func ) {
  203. if ( el.nodeType === 1 ) {
  204. //为元素则递归
  205. if ( recursion ) {
  206. 主控( el, func );
  207. }
  208. } else if ( el.nodeType === 3 ) {
  209. if ( !el.nodeValue || !el.nodeValue.length ) return;
  210. //为文本节点则处理数据
  211. func( el, 'Text', el.nodeValue );
  212. }
  213. }
  214.  
  215. return 主控;
  216. } )();
  217.  
  218. if ( typeof observerMap === 'undefined' ) observerMap = new Map();
  219. /**
  220. * 修改后的函数,在触发事件后会对其他相同config的obs排队依次触发,节流10并去重大大提升性能.
  221. * dom修改事件,包括属性,内容,节点修改
  222. * @param document 侦听对象
  223. * @param func 执行函数,可选参数(records),表示更改的节点
  224. * @param config 侦听的配置
  225. */
  226. function dom修改事件( document, func, config = {
  227. attributes: true,
  228. childList: true,
  229. characterData: true,
  230. subtree: true
  231. } ) {
  232. const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  233. //将配置对象序列化为字符串,做为key.
  234. const serializedConfig = JSON.stringify( Object.entries( config ).sort() );
  235. let throttleTimeout;
  236. let recordsArr = [];
  237. const observer = new MutationObserver( function( records ) {
  238. clearTimeout( throttleTimeout );
  239. // 记录变化
  240. recordsArr.push( ...records );
  241. throttleTimeout = setTimeout( () => {
  242. // 从recordedMutations数组中移除重复项
  243. recordsArr = removeDuplicates( recordsArr );
  244. let observers = observerMap.get( serializedConfig ) || [];
  245. // 在每次变化前暂停相同 config 的所有观察器实例
  246. observers.forEach( obs => obs.observer.disconnect() );
  247. // 对拥有相同观察器实例的文档执行各自的函数
  248. observers.forEach( obs => {
  249. try {
  250. obs.func( recordsArr );
  251. } catch ( e ) {
  252. console.error( e );
  253. }
  254. } );
  255. // 在执行完毕后重新启用相同 config 的所有观察器实例
  256. observers.forEach( obs => obs.observer.observe( document, config ) );
  257. // 清空记录的修改
  258. recordsArr = [];
  259. }, 10 );
  260. } );
  261.  
  262. // 将观察器实例和对应的函数添加到对应 config 的数组中
  263. let observers = observerMap.get( serializedConfig ) || [];
  264. observers.push( {
  265. observer,
  266. func
  267. } );
  268. observerMap.set( serializedConfig, observers );
  269.  
  270. // 开启侦听
  271. observer.observe( document, config );
  272. /**
  273. * 从后面开始去重,并保留靠后的元素.
  274. * @param arr
  275. * @returns {*}
  276. */
  277. function removeDuplicates( arr ) {
  278. return arr.reduceRight( ( unique, item ) => {
  279. // 检查当前元素是否已存在于结果数组中,如果不存在,则将其添加到数组中
  280. if ( !unique.some( i => (
  281. i.type === item.type &&
  282. i.target === item.target &&
  283. i.attributeName === item.attributeName
  284. ) ) ) {
  285. unique.push( item ); // 将不重复的元素添加到结果数组中
  286. }
  287. return unique;
  288. }, [] );
  289. }
  290. }
  291.  
  292.  
  293. /*当body发生变化时执行*/
  294. dom修改事件( document.body, ( records ) => {
  295. console.time( '替换,时间' );
  296. for ( let i = 0, len = records.length; i < len; i++ ) {
  297. 递归( records[ i ].target, 替换 );
  298. }
  299. console.timeEnd( '替换,时间' );
  300. } );
  301.  
  302. addStyle( `
  303. ruby {
  304. text-indent: 0px;
  305. }
  306. ruby > rt {
  307. display: block;
  308. font-size: 50%;
  309. text-align: start;
  310. }
  311. rt {
  312. text-indent: 0px;
  313. line-height: normal;
  314. -webkit-text-emphasis: none;
  315. }
  316. ` );
  317.  
  318. function 替换( node, attribute, value ) {
  319. value = value.trim();
  320. if ( attribute == 'Text' ) {
  321. if ( node.parentNode.nodeName == 'RT' || node.parentNode.nodeName == 'RUBY' ) {
  322. return;
  323. }
  324. }
  325. let text = value;
  326.  
  327. // 遍历映射表进行替换
  328. for ( let abbr in map ) {
  329. let regex = new RegExp( `\\b${abbr}\\b`, 'g' ); // 精确匹配简写
  330. if ( regex.test( text ) ) {
  331. // 使用ruby标签替换简写
  332. text = text.replace( regex, `<ruby>${map[abbr]}<rt>${abbr}</rt></ruby>` );
  333. }
  334. }
  335.  
  336. // 创建一个包含替换后的HTML的新节点
  337. let span = document.createElement( 'span' );
  338. span.innerHTML = text;
  339.  
  340. // 替换原有的文本节点
  341. node.parentNode.replaceChild( span, node );
  342. }
  343. //添加css样式
  344. function addStyle( rules ) {
  345. let styleElement = document.createElement( 'style' );
  346. styleElement[ "type" ] = 'text/css';
  347. document.getElementsByTagName( 'head' )[ 0 ].appendChild( styleElement );
  348. styleElement.appendChild( document.createTextNode( rules ) );
  349. }
  350. } )();