NGA优化摸鱼体验

NGA论坛显示优化,全面功能增强,优雅的摸鱼

  1. // ==UserScript==
  2. // @name NGA优化摸鱼体验
  3. // @namespace https://github.com/kisshang1993/NGA-BBS-Script
  4. // @version 4.5.4
  5. // @author HLD
  6. // @description NGA论坛显示优化,全面功能增强,优雅的摸鱼
  7. // @license MIT
  8. // @require https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-y/jquery/3.4.0/jquery.min.js#sha512=Pa4Jto+LuCGBHy2/POQEbTh0reuoiEXQWXGn8S7aRlhcwpVkO8+4uoZVSOqUjdCsE+77oygfu2Tl+7qGHGIWsw==
  9. // @require https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-y/spectrum/1.8.0/spectrum.min.js#sha512=Bx3FZ9S4XKYq5P1Yxfqp36JifotqAAAl5eotNaGWE1zSSLifBZlbKExLh2NKHA4CTlqHap7xdFzo39W+CTKrWQ==
  10. // @require https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-y/localforage/1.10.0/localforage.min.js#sha512=+BMamP0e7wn39JGL8nKAZ3yAQT2dL5oaXWr4ZYlTGkKOaoXM/Yj7c4oy50Ngz5yoUutAG17flueD4F6QpTlPng==
  11. // @require https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-y/echarts/5.3.0/echarts.min.js#sha512=dvHO84j/D1YX7AWkAPC/qwRTfEgWRHhI3n7J5EAqMwm4r426sTkcOs6OmqCtmkg0QXNKtiFa67Tp77JWCRRINg==
  12. // @require https://gf.qytechs.cn/scripts/424901-nga-script-resource/code/NGA-Script-Resource.js?version=1268947
  13. // @icon https://i.loli.net/2021/04/07/8x3yFj2pWEKluSY.png
  14. // @match *://bbs.nga.cn/*
  15. // @match *://ngabbs.com/*
  16. // @match *://nga.178.com/*
  17. // @match *://g.nga.cn/*
  18. // @grant GM_registerMenuCommand
  19. // @grant GM_setValue
  20. // @grant GM_getValue
  21. // @grant GM_deleteValue
  22. // @grant GM_listValues
  23. // @grant unsafeWindow
  24. // @inject-into content
  25. // ==/UserScript==
  26.  
  27. (function () {
  28. 'use strict';
  29.  
  30. /**
  31. * NGA摸鱼主脚本
  32. * @class NGABBSScript
  33. * @constructor
  34. */
  35. class NGABBSScript {
  36. constructor() {
  37. // 配置
  38. this.setting = {
  39. original: [],
  40. normal: {},
  41. advanced: {}
  42. }
  43. // 模块
  44. this.modules = []
  45. // 样式
  46. this.style = ''
  47. // 数据存储
  48. this.store = {}
  49. // 引用库
  50. this.libs = {$, echarts, localforage}
  51. }
  52. /**
  53. * 获取模块对象
  54. * @method getModule
  55. * @param {String} name 模块name
  56. * @return {Object} 模块对象
  57. */
  58. getModule(name) {
  59. for (const m of this.modules) {
  60. if (m.name && m.name === name) {
  61. return m
  62. }
  63. }
  64. return null
  65. }
  66. /**
  67. * 全程渲染函数
  68. * @method renderAlways
  69. */
  70. renderAlways() {
  71. for (const module of this.modules) {
  72. try {
  73. module.renderAlwaysFunc && module.renderAlwaysFunc(this)
  74. } catch (error) {
  75. this.printLog(`[${module.name}]模块在[renderAlwaysFunc()]中运行失败!`)
  76. console.log(error)
  77. }
  78. }
  79. }
  80. /**
  81. * 列表页渲染函数
  82. * @method renderThreads
  83. */
  84. renderThreads() {
  85. $('.topicrow[hld-threads-render!=ok]').each((index, dom) => {
  86. const $el = $(dom)
  87. for (const module of this.modules) {
  88. try {
  89. module.renderThreadsFunc && module.renderThreadsFunc($el, this)
  90. } catch (error) {
  91. this.printLog(`[${module.name}]模块在[renderThreadsFunc()]中运行失败!`)
  92. console.log(error)
  93. }
  94. }
  95. $el.attr('hld-threads-render', 'ok')
  96. })
  97. }
  98. /**
  99. * 详情页渲染函数
  100. * @method renderForms
  101. */
  102. renderForms() {
  103. $('.forumbox.postbox[hld-forms-render!=ok]').each((index, dom) => {
  104. const $el = $(dom)
  105. // 等待NGA页面渲染完成
  106. if ($el.find('.small_colored_text_btn').length == 0) return true
  107. for (const module of this.modules) {
  108. try {
  109. module.renderFormsFunc && module.renderFormsFunc($el, this)
  110. } catch (error) {
  111. this.printLog(`[${module.name}]模块在[renderFormsFunc()]中运行失败!`)
  112. console.log(error)
  113. }
  114. }
  115. $el.attr('hld-forms-render', 'ok')
  116. })
  117. }
  118. /**
  119. * 添加模块
  120. * @method addModule
  121. * @param {Object} module 模块对象
  122. * @param {Boolean} plugin 是否为插件
  123. */
  124. addModule(module) {
  125. // 组件预处理函数
  126. if (module.preProcFunc) {
  127. try {
  128. module.preProcFunc(this)
  129. } catch (error) {
  130. this.printLog(`[${module.name}]模块在[preProcFunc()]中运行失败!`)
  131. console.log(error)
  132. }
  133. }
  134. // 添加设置
  135. const addSetting = setting => {
  136. // 标准模块配置
  137. if (setting.shortCutCode && this.setting.normal.shortcutKeys) {
  138. this.setting.normal.shortcutKeys.push(setting.shortCutCode)
  139. }
  140. if (setting.key) {
  141. this.setting[setting.type || 'normal'][setting.key] = setting.default ?? ''
  142. this.setting.original.push(setting)
  143. }
  144. }
  145. // 功能板块
  146. if (module.setting && !Array.isArray(module.setting)) {
  147. addSetting(module.setting)
  148. }
  149. if (module.settings && Array.isArray(module.settings)) {
  150. for (const setting of module.settings) {
  151. addSetting(setting)
  152. }
  153. }
  154. // 添加样式
  155. if (module.style) {
  156. this.style += module.style
  157. }
  158. this.modules.push(module)
  159. }
  160. /**
  161. * 判断当前页面是否为列表页
  162. * @method isThreads
  163. * @return {Boolean} 判断状态
  164. */
  165. isThreads() {
  166. return $('#m_threads').length > 0
  167. }
  168. /**
  169. * 判断当前页面是否为详情页
  170. * @method isForms
  171. * @return {Boolean} 判断状态
  172. */
  173. isForms() {
  174. return $('#m_posts').length > 0
  175. }
  176. /**
  177. * 抛出异常
  178. * @method throwError
  179. * @param {String} msg 异常信息
  180. */
  181. throwError(msg) {
  182. alert(msg)
  183. throw(msg)
  184. }
  185. /**
  186. * 初始化
  187. * @method init
  188. */
  189. init() {
  190. // 开始初始化
  191. this.printLog('初始化...')
  192. localforage.config({name: 'NGA BBS Script DB'})
  193. const startInitTime = new Date().getTime()
  194. const modulesTable = []
  195. //同步配置
  196. this.loadSetting()
  197. // 组件初始化函数
  198. for (const module of this.modules) {
  199. if (module.initFunc) {
  200. try {
  201. module.initFunc(this)
  202. } catch (error) {
  203. this.printLog(`[${module.name}]模块在[initFunc()]中运行失败!`)
  204. console.log(error)
  205. }
  206. }
  207. }
  208. // 组件后处理函数
  209. for (const module of this.modules) {
  210. if (module.postProcFunc) {
  211. try {
  212. module.postProcFunc(this)
  213. } catch (error) {
  214. this.printLog(`[${module.name}]模块在[postProcFunc()]中运行失败!`)
  215. console.log(error)
  216. }
  217. }
  218. }
  219. // 动态样式
  220. for (const module of this.modules) {
  221. if (module.asyncStyle) {
  222. try {
  223. this.style += module.asyncStyle(this)
  224. } catch (error) {
  225. this.printLog(`[${module.name}]模块在[asyncStyle()]中运行失败!`)
  226. console.log(error)
  227. }
  228. }
  229. modulesTable.push({
  230. name: module.title || module.name || 'UNKNOW',
  231. type: module.type == 'plugin' ? '插件' : '标准模块',
  232. version: module.version || '-'
  233. })
  234. }
  235. // 插入样式
  236. const style = document.createElement("style")
  237. style.appendChild(document.createTextNode(this.style))
  238. document.getElementsByTagName('head')[0].appendChild(style)
  239. // 初始化完成
  240. const endInitTime = new Date().getTime()
  241. console.table(modulesTable)
  242. this.printLog(`[v${this.getInfo().version}] 初始化完成: 共加载${this.modules.length}个模块,总耗时${endInitTime-startInitTime}ms`)
  243. console.log('%c反馈问题请前往: https://github.com/kisshang1993/NGA-BBS-Script/issues', 'color:orangered;font-weight:bolder')
  244. }
  245. /**
  246. * 通知弹框
  247. * @method popNotification
  248. * @param {String} msg 消息内容
  249. * @param {Number} duration 显示时长(ms)
  250. */
  251. popNotification(msg, duration=1000) {
  252. $('#hld__noti_container').length == 0 && $('body').append('<div id="hld__noti_container"></div>')
  253. let $msgBox = $(`<div class="hld__noti-msg">${msg}</div>`)
  254. $('#hld__noti_container').append($msgBox)
  255. $msgBox.slideDown(100)
  256. setTimeout(() => { $msgBox.fadeOut(500) }, duration)
  257. setTimeout(() => { $msgBox.remove() }, duration + 500)
  258. }
  259. /**
  260. * 消息弹框
  261. * @method popMsg
  262. * @param {String} msg 消息内容
  263. * @param {String} type 消息类型 [ok, err, warn]
  264. */
  265. popMsg(msg, type='ok') {
  266. $('.hld__msg').length > 0 && $('.hld__msg').remove()
  267. let $msg = $(`<div class="hld__msg hld__msg-${type}">${msg}</div>`)
  268. $('body').append($msg)
  269. $msg.slideDown(200)
  270. setTimeout(() => { $msg.fadeOut(500) }, type == 'ok' ? 2000 : 5000)
  271. setTimeout(() => { $msg.remove() }, type == 'ok' ? 2500 : 5500)
  272. }
  273. /**
  274. * 打印控制台消息
  275. * @method printLog
  276. * @param {String} msg 消息内容
  277. */
  278. printLog(msg) {
  279. console.log(`%cNGA%cScript%c ${msg}`,
  280. 'background: #222;color: #fff;font-weight:bold;padding:2px 2px 2px 4px;border-radius:4px 0 0 4px;',
  281. 'background: #fe9a00;color: #000;font-weight:bold;padding:2px 4px 2px 2px;border-radius:0px 4px 4px 0px;',
  282. 'background:none;color:#000;'
  283. )
  284. }
  285. /**
  286. * 读取值
  287. * @method saveSetting
  288. * @param {String} key
  289. */
  290. getValue(key) {
  291. try {
  292. return GM_getValue(key) || window.localStorage.getItem(key)
  293. } catch {
  294. // 兼容性代码: 计划将在5.0之后废弃
  295. return window.localStorage.getItem(key)
  296. }
  297. }
  298. /**
  299. * 写入值
  300. * @method setValue
  301. * @param {String} key
  302. * @param {String} value
  303. */
  304. setValue(key, value) {
  305. try {
  306. GM_setValue(key, value)
  307. } catch {}
  308. }
  309. /**
  310. * 删除值
  311. * @method deleteValue
  312. * @param {String} key
  313. */
  314. deleteValue(key) {
  315. try {
  316. GM_deleteValue(key)
  317. } catch {}
  318. // 兼容性代码: 计划将在5.0之后飞起
  319. window.localStorage.removeItem(key)
  320. }
  321. /**
  322. * 保存配置到本地
  323. * @method saveSetting
  324. * @param {String} msg 自定义消息信息
  325. */
  326. saveSetting(msg='保存配置成功,刷新页面生效') {
  327. // 基础设置
  328. for (let k in this.setting.normal) {
  329. $('input#hld__cb_' + k).length > 0 && (this.setting.normal[k] = $('input#hld__cb_' + k)[0].checked)
  330. }
  331. script.setValue('hld__NGA_setting', JSON.stringify(this.setting.normal))
  332. // 高级设置
  333. for (let k in this.setting.advanced) {
  334. if ($('#hld__adv_' + k).length > 0) {
  335. const originalSetting = this.setting.original.find(s => s.type == 'advanced' && s.key == k)
  336. const valueType = typeof originalSetting.default
  337. const inputType = $('#hld__adv_' + k)[0].nodeName
  338. if (inputType == 'SELECT') {
  339. this.setting.advanced[k] = $('#hld__adv_' + k).val()
  340. } else {
  341. if (valueType == 'boolean') {
  342. this.setting.advanced[k] = $('#hld__adv_' + k)[0].checked
  343. }
  344. if (valueType == 'number') {
  345. this.setting.advanced[k] = +$('#hld__adv_' + k).val()
  346. }
  347. if (valueType == 'string') {
  348. this.setting.advanced[k] = $('#hld__adv_' + k).val()
  349. }
  350. }
  351. }
  352. }
  353. script.setValue('hld__NGA_advanced_setting', JSON.stringify(this.setting.advanced))
  354. msg && this.popMsg(msg)
  355. }
  356. /**
  357. * 从本地读取配置
  358. * @method loadSetting
  359. */
  360. loadSetting() {
  361. // 基础设置
  362. try {
  363. const settingStr = script.getValue('hld__NGA_setting')
  364. if (settingStr) {
  365. let localSetting = JSON.parse(settingStr)
  366. for (let k in this.setting.normal) {
  367. !localSetting.hasOwnProperty(k) && (localSetting[k] = this.setting.normal[k])
  368. if (k == 'shortcutKeys') {
  369. if (localSetting[k].length < this.setting.normal[k].length) {
  370. const offset_count = this.setting.normal[k].length - localSetting[k].length
  371. localSetting[k] = localSetting[k].concat(this.setting.normal[k].slice(-offset_count))
  372. }
  373. // 更改默认按键
  374. let index = 0
  375. for (const module of this.modules) {
  376. if (module.setting && module.setting.shortCutCode) {
  377. if (localSetting[k][index] != module.setting.shortCutCode) {
  378. module.setting.rewriteShortCutCode = localSetting[k][index]
  379. }
  380. index += 1
  381. }else if (module.settings) {
  382. for (const setting of module.settings) {
  383. if (setting.shortCutCode) {
  384. if (localSetting[k][index] != setting.shortCutCode) {
  385. setting.rewriteShortCutCode = localSetting[k][index]
  386. }
  387. index += 1
  388. }
  389. }
  390. }
  391. }
  392. }
  393. }
  394. for (let k in localSetting) {
  395. !this.setting.normal.hasOwnProperty(k) && delete localSetting[k]
  396. }
  397. this.setting.normal = localSetting
  398. }
  399. // 高级设置
  400. const advancedSettingStr = script.getValue('hld__NGA_advanced_setting')
  401. if (advancedSettingStr) {
  402. let localAdvancedSetting = JSON.parse(advancedSettingStr)
  403. for (let k in this.setting.advanced) {
  404. !localAdvancedSetting.hasOwnProperty(k) && (localAdvancedSetting[k] = this.setting.advanced[k])
  405. }
  406. for (let k in localAdvancedSetting) {
  407. !this.setting.advanced.hasOwnProperty(k) && delete localAdvancedSetting[k]
  408. }
  409. this.setting.advanced = localAdvancedSetting
  410. }
  411. } catch(e) {
  412. script.throwError(`【NGA-Script】读取配置文件出现错误,无法加载配置文件!\n错误问题: ${e}\n\n请尝试使用【修复脚本】来修复此问题`)
  413. }
  414.  
  415. }
  416. /**
  417. * 检查是否更新
  418. * @method checkUpdate
  419. */
  420. checkUpdate() {
  421. // 字符串版本转数字
  422. const vstr2num = str => {
  423. let num = 0
  424. str.split('.').forEach((n, i) => num += i < 2 ? +n * 1000 / Math.pow(10, i) : +n)
  425. return num
  426. }
  427. // 字符串中版本截取
  428. const vstr2mid = str => {
  429. return str.substring(0, str.lastIndexOf('.'))
  430. }
  431. //检查更新
  432. const cver = script.getValue('hld__NGA_version')
  433. if (cver) {
  434. const local_version = vstr2num(cver)
  435. const current_version = vstr2num(GM_info.script.version)
  436. if (current_version > local_version) {
  437. const lv_mid = +vstr2mid(cver)
  438. const cv_mid = +vstr2mid(GM_info.script.version)
  439. script.setValue('hld__NGA_version', GM_info.script.version)
  440. if (cv_mid > lv_mid) {
  441. const focus = ''
  442. $('body').append(`<div id="hld__updated" class="animated-1s bounce"><p><a href="javascript:void(0)" class="hld__setting-close">×</a><b>NGA-Script已更新至v${GM_info.script.version}</b></p>${focus}<p><a class="hld__readme" href="https://gf.qytechs.cn/zh-CN/scripts/393991-nga%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C" target="_blank">查看更新内容</a></p></div>`)
  443. $('body').on('click', '#hld__updated a', function () {
  444. $(this).parents('#hld__updated').remove()
  445. })
  446. }
  447. }
  448. } else script.setValue('hld__NGA_version', GM_info.script.version)
  449. }
  450. /**
  451. * 创建储存对象实例
  452. * @param {String} instanceName 实例名称
  453. */
  454. createStorageInstance(instanceName) {
  455. if (!instanceName || Object.keys(this.store).includes(instanceName)) {
  456. this.throwError('【NGA-Script】创建储存对象实例失败,实例名称不能为空或实例名称已存在')
  457. }
  458. const lfInstance = localforage.createInstance({name: instanceName})
  459. this.store[instanceName] = lfInstance
  460. return lfInstance
  461. }
  462. /**
  463. * 运行脚本
  464. * @method run
  465. */
  466. run() {
  467. this.checkUpdate()
  468. this.init()
  469. setInterval(() => {
  470. this.renderAlways()
  471. this.isThreads() && this.renderThreads()
  472. this.isForms() && this.renderForms()
  473. }, 100)
  474. }
  475. /**
  476. * 获取脚本信息
  477. * @method getInfo
  478. * @return {Object} 脚本信息对象
  479. */
  480. getInfo() {
  481. return {
  482. version: GM_info.script.version,
  483. author: 'HLD',
  484. github: 'https://github.com/kisshang1993/NGA-BBS-Script',
  485. update: 'https://gf.qytechs.cn/zh-CN/scripts/393991-nga%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C'
  486. }
  487. }
  488. }
  489.  
  490. /* 注册(不可用)菜单按钮 */
  491. try {
  492. // 设置面板
  493. GM_registerMenuCommand('设置面板', function () {
  494. $('#hld__setting_cover').css('display', 'block')
  495. $('html, body').animate({scrollTop: 0}, 500)
  496. })
  497. // 清理缓存
  498. GM_registerMenuCommand('清理缓存', function () {
  499. if (window.confirm('此操作为清理Local Storage与IndexedDB部分缓存内容,不会清理配置\n\n继续请点击【确定】')) {
  500. script.deleteValue('hld__NGA_post_author')
  501. localforage.clear()
  502. alert('操作成功,请刷新页面重试')
  503. }
  504. })
  505. // 修复脚本
  506. GM_registerMenuCommand('修复脚本', function () {
  507. if (window.confirm('如脚本运行失败或无效,尝试修复脚本,这会清除脚本的所有数据\n* 数据包含配置,各种名单等\n* 此操作不可逆转,请谨慎操作\n\n继续请点击【确定】')) {
  508. try {
  509. GM_listValues().forEach(key => GM_deleteValue(key))
  510. } catch {}
  511. // 兼容性代码: 计划将在5.0之后废弃
  512. window.localStorage.clear()
  513. alert('操作成功,请刷新页面重试')
  514. }
  515. })
  516. // 反馈问题
  517. GM_registerMenuCommand('反馈问题', function () {
  518. if (window.confirm('如脚本运行失败而且修复后也无法运行,请反馈问题报告\n* 问题报告请包含使用的: [浏览器],[脚本管理器],[脚本版本]\n* 描述问题最好以图文并茂的形式\n* 如脚本运行失败,建议提供F12控制台的红色错误输出以辅助排查\n\n默认打开的为Greasy Fork镜像的反馈页面,有能力最好去Github Issue反馈问题,可以获得优先处理\n\n即将打开反馈页面,继续请点击【确定】')) {
  519. window.open('https://gf.qytechs.cn/zh-CN/scripts/393991-nga%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C/feedback')
  520. }
  521. })
  522. } catch (e) {
  523. // 不支持此命令
  524. console.warn(`【NGA Script】警告: 此脚本管理器不支持菜单按钮,可能会导致新特性无法正常使用,建议更改脚本管理器为
  525. Tampermonkey[https://www.tampermonkey.net/] 或 Violentmonkey[https://violentmonkey.github.io/]`)
  526. }
  527.  
  528. /* 标准模块 */
  529. /**
  530. * 设置模块
  531. * @name SettingPanel
  532. * @description 提供脚本的设置面板,提供配置修改,保存等基础功能
  533. */
  534. const SettingPanel = {
  535. name: 'SettingPanel',
  536. title: '设置模块',
  537. initFunc() {
  538. //设置面板
  539. let $panelDom = $(`
  540. <div id="hld__setting_cover" class="animated zoomIn">
  541. <div id="hld__setting_panel">
  542. <a href="javascript:void(0)" id="hld__setting_close" class="hld__setting-close" close-type="hide">×</a>
  543. <p class="hld__sp-title"><a title="更新地址" href="https://gf.qytechs.cn/zh-CN/scripts/393991-nga%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C" target="_blank">NGA优化摸鱼体验<span class="hld__script-info">v${script.getInfo().version}</span></a></p>
  544. <div class="hld__field">
  545. <p class="hld__sp-section">显示优化</p>
  546. <div id="hld__normal_left"></div>
  547. </div>
  548. <div class="hld__field">
  549. <p class="hld__sp-section">功能增强</p>
  550. <div id="hld__normal_right"></div>
  551. </div>
  552. <div style="clear:both"></div>
  553. <div class="hld__advanced-setting">
  554. <button id="hld__advanced_button">+</button><span>高级设置</span>
  555. <div class="hld__advanced-setting-panel">
  556. <p><svg t="1590560820184" class="icon" viewBox="0 0 1040 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2738" width="200" height="200"><path d="M896.355855 975.884143 127.652332 975.884143c-51.575656 0-92.993974-19.771299-113.653503-54.238298-20.708648-34.515095-18.194384-79.5815 6.9022-123.5632L408.803663 117.885897c25.244964-44.376697 62.767556-69.77004 102.953813-69.77004 40.136116 0 77.658707 25.393343 103.002932 69.671803L1003.006873 798.131763c25.097608 44.030819 27.711132 89.049129 6.952342 123.514081C989.348806 956.159916 947.881368 975.884143 896.355855 975.884143L896.355855 975.884143 896.355855 975.884143 896.355855 975.884143 896.355855 975.884143zM511.805572 119.511931c-12.769838 0-27.414373 12.376888-39.298028 33.134655L84.656075 832.892451c-12.130272 21.350261-14.989389 40.530089-7.741311 52.611242 7.297197 12.08013 25.787316 19.033495 50.737568 19.033495l768.703523 0c24.997324 0 43.439348-6.903224 50.736545-19.033495 7.197936-12.031011 4.387937-31.210839-7.791453-52.5611L551.055504 152.646586C539.220968 131.888819 524.527314 119.511931 511.805572 119.511931L511.805572 119.511931 511.805572 119.511931 511.805572 119.511931 511.805572 119.511931zM512.004093 653.807726c-20.1182 0-36.488029-15.975856-36.488029-35.69906L475.516064 296.773124c0-19.723204 16.369829-35.698037 36.488029-35.698037 20.117177 0 36.485983 15.975856 36.485983 35.698037l0 321.335543C548.490076 637.832893 532.12127 653.807726 512.004093 653.807726L512.004093 653.807726 512.004093 653.807726 512.004093 653.807726zM511.757476 828.308039c31.359218 0 56.851822-24.950252 56.851822-55.717999s-25.491581-55.716976-56.851822-55.716976c-31.408337 0-56.851822 24.949228-56.851822 55.716976S480.349139 828.308039 511.757476 828.308039L511.757476 828.308039 511.757476 828.308039 511.757476 828.308039z" p-id="2739"></path></svg> 鼠标停留在<span class="hld__help" title="详细描述">选项文字</span>上可以显示详细描述,设置有误可能会导致插件异常或者无效!</p>
  557. <table id="hld__advanced_left"></table>
  558. <table id="hld__advanced_right"></table>
  559. </div>
  560. </div>
  561. <div class="hld__buttons">
  562. <span id="hld_setting_panel_buttons"></span>
  563. <span>
  564. <button class="hld__btn" id="hld__save__data">保存设置</button>
  565. </span>
  566. </div>
  567. </div>
  568. </div>
  569. `)
  570. const insertDom = setting => {
  571. if (setting.type === 'normal') {
  572. $panelDom.find(`#hld__normal_${setting.menu || 'left'}`).append(`
  573. <p><label ${setting.desc ? 'class="hld__help" help="'+setting.desc+'"' : ''}><input type="checkbox" id="hld__cb_${setting.key}"> ${setting.title || setting.key}${setting.shortCutCode ? '(快捷键切换[<b>'+script.getModule('ShortCutKeys').getCodeName(setting.rewriteShortCutCode || setting.shortCutCode)+'</b>])' : ''}</label></p>
  574. `)
  575. if (setting.extra) {
  576. $panelDom.find(`#hld__cb_${setting.key}`).attr('enable', `hld__${setting.key}_${setting.extra.mode || 'fold'}`)
  577. $panelDom.find(`#hld__normal_${setting.menu || 'left'}`).append(`
  578. <div class="hld__sp-${setting.extra.mode || 'fold'}" id="hld__${setting.key}_${setting.extra.mode || 'fold'}" data-id="hld__${setting.key}">
  579. <p><button id="${setting.extra.id}">${setting.extra.label}</button></p>
  580. </div>
  581. `)
  582. }
  583. }
  584. if (setting.type === 'advanced') {
  585. let formItem = ''
  586. const valueType = typeof setting.default
  587. if (valueType === 'boolean') {
  588. formItem = `<input type="checkbox" id="hld__adv_${setting.key}">`
  589. }
  590. if (valueType === 'number') {
  591. formItem = `<input type="number" id="hld__adv_${setting.key}">`
  592. }
  593. if (valueType === 'string') {
  594. if (setting.options) {
  595. let t = ''
  596. for (const option of setting.options) {
  597. t += `<option value="${option.value}">${option.label}</option>`
  598. }
  599. formItem = `<select id="hld__adv_${setting.key}">${t}</select>`
  600. } else {
  601. formItem = `<input type="text" id="hld__adv_${setting.key}">`
  602. }
  603. }
  604. $panelDom.find(`#hld__advanced_${setting.menu || 'left'}`).append(`
  605. <tr>
  606. <td><span class="hld__help" help="${setting.desc || ''}">${setting.title || setting.key}</span></td>
  607. <td>${formItem}</td>
  608. </tr>`)
  609. }
  610. }
  611. for (const module of script.modules) {
  612. if (module.setting && module.setting.key) {
  613. insertDom(module.setting)
  614. }
  615. if (module.settings) {
  616. for (const setting of module.settings) {
  617. setting.key && insertDom(setting)
  618. }
  619. }
  620. }
  621. /**
  622. * Bind:Mouseover Mouseout
  623. * 提示信息Tips
  624. */
  625. $('body').on('mouseover', '.hld__help', function(e){
  626. if (!$(this).attr('help')) return
  627. const $help = $(`<div class="hld__help-tips">${$(this).attr('help').replace(/\n/g, '<br>')}</div>`)
  628. $help.css({
  629. top: ($(this).offset().top + $(this).height() + 5) + 'px',
  630. left: $(this).offset().left + 'px'
  631. })
  632. $('body').append($help)
  633. }).on('mouseout', '.hld__help', ()=>$('.hld__help-tips').remove())
  634. $('body').append($panelDom)
  635. //本地恢复设置
  636. //基础设置
  637. for (let k in script.setting.normal) {
  638. if ($('#hld__cb_' + k).length > 0) {
  639. $('#hld__cb_' + k)[0].checked = script.setting.normal[k]
  640. const enableDomID = $('#hld__cb_' + k).attr('enable')
  641. if (enableDomID) {
  642. script.setting.normal[k] ? $('#' + enableDomID).show() : $('#' + enableDomID).hide()
  643. $('#' + enableDomID).find('input').each(function () {
  644. $(this).val() == script.setting.normal[$(this).attr('name').substring(8)] && ($(this)[0].checked = true)
  645. })
  646. $('#hld__cb_' + k).on('click', function () {
  647. $(this)[0].checked ? $('#' + enableDomID).slideDown() : $('#' + enableDomID).slideUp()
  648. })
  649. }
  650. }
  651. }
  652. //高级设置
  653. for (let k in script.setting.advanced) {
  654. if ($('#hld__adv_' + k).length > 0) {
  655. const valueType = typeof script.setting.advanced[k]
  656. if (valueType == 'boolean') {
  657. $('#hld__adv_' + k)[0].checked = script.setting.advanced[k]
  658. }
  659. if (valueType == 'number' || valueType == 'string') {
  660. $('#hld__adv_' + k).val(script.setting.advanced[k])
  661. }
  662. }
  663. }
  664. /**
  665. * Bind:Click
  666. * 设置面板-展开切换高级设置
  667. */
  668. $('body').on('click', '#hld__advanced_button', function () {
  669. if ($('.hld__advanced-setting-panel').is(':hidden')) {
  670. $('.hld__advanced-setting-panel').css('display', 'flex')
  671. $(this).text('-')
  672. } else {
  673. $('.hld__advanced-setting-panel').css('display', 'none')
  674. $(this).text('+')
  675. }
  676. })
  677. /**
  678. * Bind:Click
  679. * 关闭面板(通用)
  680. */
  681. $('body').on('click', '.hld__list-panel .hld__setting-close', function () {
  682. if ($(this).attr('close-type') == 'hide') {
  683. $(this).parent().hide()
  684. } else {
  685. $(this).parent().remove()
  686. }
  687. })
  688. /**
  689. * Bind:Click
  690. * 保存配置
  691. */
  692. $('body').on('click', '#hld__save__data', () => {
  693. script.saveSetting()
  694. $('#hld__setting_cover').fadeOut(200)
  695. })
  696. },
  697. renderAlwaysFunc() {
  698. if($('.hld__setting-box').length == 0) {
  699. $('#startmenu > tbody > tr > td.last').append('<div><div class="item hld__setting-box"></div></div>')
  700. let $entry = $('<a id="hld__setting" title="打开NGA优化摸鱼插件设置面板">NGA优化摸鱼插件设置</a>')
  701. $entry.click(()=>{
  702. $('#hld__setting_cover').css('display', 'block')
  703. $('html, body').animate({scrollTop: 0}, 500)
  704. })
  705. $('#hld__setting_close').click(()=>$('#hld__setting_cover').fadeOut(200))
  706. $('.hld__setting-box').append($entry)
  707. }
  708. },
  709. addButton(button) {
  710. const $button = $(`<button class="hld__btn" id="${button.id}" title="${button.desc}">${button.title}</button>`)
  711. if (typeof button.click == 'function') {
  712. $button.on('click', function() {
  713. button.click($(this))
  714. })
  715. }
  716. $('#hld_setting_panel_buttons').append($button)
  717. },
  718. style: `
  719. .animated {animation-duration:.3s;animation-fill-mode:both;}
  720. .animated-1s {animation-duration:1s;animation-fill-mode:both;}
  721. .zoomIn {animation-name:zoomIn;}
  722. .bounce {-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom;}
  723. .fadeInUp {-webkit-animation-name:fadeInUp;animation-name:fadeInUp;}
  724. #loader {display:none;position:absolute;top:50%;left:50%;margin-top:-10px;margin-left:-10px;width:20px;height:20px;border:6px dotted #FFF;border-radius:50%;-webkit-animation:1s loader linear infinite;animation:1s loader linear infinite;}
  725. @keyframes loader {0% {-webkit-transform:rotate(0deg);transform:rotate(0deg);}100% {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
  726. @keyframes zoomIn {from {opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3);}50% {opacity:1;}}
  727. @keyframes bounce {from,20%,53%,80%,to {-webkit-animation-timing-function:cubic-bezier(0.215,0.61,0.355,1);animation-timing-function:cubic-bezier(0.215,0.61,0.355,1);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%,43% {-webkit-animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);animation-timing-function:cubic-bezier(0.755,0.05,0.855,0.06);-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}
  728. @keyframes fadeInUp {from {opacity:0;-webkit-transform:translate3d(-50%,100%,0);transform:translate3d(-50%,100%,0);}to {opacity:1;-webkit-transform:translate3d(-50%,0,0);transform:translate3d(-50%,0,0);}}
  729. .hld__msg{display:none;position:fixed;top:10px;left:50%;transform:translateX(-50%);color:#fff;text-align:center;z-index:99996;padding:10px 30px 10px 45px;font-size:16px;border-radius:10px;background-image:url("${SVG_ICON_MSG}");background-size:25px;background-repeat:no-repeat;background-position:15px}
  730. .hld__msg a{color:#fff;text-decoration: underline;}
  731. .hld__msg-ok{background:#4bcc4b}
  732. .hld__msg-err{background:#c33}
  733. .hld__msg-warn{background:#FF9900}
  734. .hld__flex{display:flex;}
  735. .hld__float-left{float: left;}
  736. .clearfix {clear: both;}
  737. #hld__noti_container {position:fixed;top:10px;left:10px;z-index:99;}
  738. .hld__noti-msg {display:none;padding:10px 20px;font-size:14px;font-weight:bold;color:#fff;margin-bottom:10px;background:rgba(0,0,0,0.6);border-radius:10px;cursor:pointer;}
  739. .hld__btn-groups {display:flex;justify-content:center !important;margin-top:10px;}
  740. button.hld__btn {padding:3px 8px;border:1px solid #591804;background:#fff8e7;color:#591804;}
  741. button.hld__btn:hover {background:#591804;color:#fff0cd;}
  742. button.hld__btn[disabled] {opacity:.5;}
  743. #hld__updated {position:fixed;top:20px;right:20px;width:230px;padding:10px;border-radius:5px;box-shadow:0 0 15px #666;border:1px solid #591804;background:#fff8e7;z-index: 9999;}
  744. #hld__updated .hld__readme {text-decoration:underline;color:#591804;}
  745. .hld__script-info {margin-left:4px;font-size:70%;color:#666;}
  746. #hld__setting {color:#6666CC;cursor:pointer;}
  747. #hld__setting_cover {display:none;padding-top: 70px;position:absolute;top:0;left:0;right:0;bottom:0;z-index:999;}
  748. #hld__setting_panel {position:relative;background:#fff8e7;width:600px;left: 50%;transform: translateX(-50%);padding:15px 20px;border-radius:10px;box-shadow:0 0 10px #666;border:1px solid #591804;}
  749. #hld__setting_panel > div.hld__field {float:left;width:50%;}
  750. #hld__setting_panel p {margin-bottom:10px;}
  751. #hld__setting_panel .hld__sp-title {font-size:15px;font-weight:bold;text-align:center;}
  752. #hld__setting_panel .hld__sp-section {font-weight:bold;margin-top:20px;}
  753. .hld__setting-close {position:absolute;top:5px;right:5px;padding:3px 6px;background:#fff0cd;color:#591804;transition:all .2s ease;cursor:pointer;border-radius:4px;text-decoration:none;z-index:9999;}
  754. .hld__setting-close:hover {background:#591804;color:#fff0cd;text-decoration:none;}
  755. #hld__setting_panel button {transition:all .2s ease;cursor:pointer;}
  756. .hld__advanced-setting {border-top: 1px solid #e0c19e;border-bottom: 1px solid #e0c19e;padding: 3px 0;margin-top:25px;}
  757. .hld__advanced-setting >span {font-weight:bold}
  758. .hld__advanced-setting >button {padding: 0px;margin-right:5px;width: 18px;text-align: center;}
  759. .hld__advanced-setting-panel {display:none;padding:5px 0;flex-wrap: wrap;}
  760. .hld__advanced-setting-panel>p {width:100%;}
  761. .hld__advanced-setting-panel>table {width:50%;}
  762. .hld__advanced-setting-panel>p {margin: 7px 0 !important;font-weight:bold;}
  763. .hld__advanced-setting-panel>p svg {height:16px;width:16px;vertical-align: top;margin-right:3px;}
  764. .hld__advanced-setting-panel>table td {padding-right:10px}
  765. .hld__advanced-setting-panel input[type=text],.hld__advanced-setting-panel input[type=number] {width:80px}
  766. .hld__advanced-setting-panel input[type=number] {border: 1px solid #e6c3a8;box-shadow: 0 0 2px 0 #7c766d inset;border-radius: 0.25em;}
  767. .hld__help {cursor:help;text-decoration: underline;}
  768. .hld__buttons {clear:both;display:flex;justify-content:space-between;padding-top:15px;}
  769. button.hld__btn {padding:3px 8px;border:1px solid #591804;background:#fff8e7;color:#591804;}
  770. button.hld__btn:hover {background:#591804;color:#fff0cd;}
  771. .hld__sp-fold {padding-left:23px;}
  772. .hld__sp-fold .hld__f-title {font-weight:bold;}
  773. .hld__help-tips {position: absolute;padding: 5px 10px;background: rgba(0,0,0,.8);color: #FFF;border-radius: 5px;z-index: 9999;}
  774. `
  775. }
  776. /**
  777. * 快捷键模块
  778. * @name ShortCutKeys
  779. * @description 为模块提供快捷键切换的能力,提供修改,保存快捷键等
  780. */
  781. const ShortCutKeys = {
  782. name: 'ShortCutKeys',
  783. title: '快捷键支持',
  784. setting: {
  785. type: 'advanced',
  786. key: 'dynamicEnable',
  787. default: true,
  788. title: '动态功能启用',
  789. desc: '此配置表示部分可以快捷键切换的功能默认行为策略\n选中时: 关闭功能(如隐藏头像)也可以通过快捷键切换显示/隐藏\n取消时: 关闭功能(如隐藏头像)将彻底关闭功能,快捷键会失效',
  790. menu: 'left'
  791. },
  792. preProcFunc() {
  793. script.setting.normal.shortcutKeys = []
  794. },
  795. initFunc() {
  796. const _this = this
  797. // 添加到配置面板的设置入口
  798. script.getModule('SettingPanel').addButton({
  799. id: 'hld__shortcut_manage',
  800. title: '编辑快捷键',
  801. desc: '编辑快捷键'
  802. })
  803. /**
  804. * Bind:keyup
  805. * 注册(不可用)监听按键
  806. */
  807. $('body').keyup(event => {
  808. if (/textarea|select|input/i.test(event.target.nodeName)
  809. || /text|password|number|email|url|range|date|month/i.test(event.target.type)) {
  810. return
  811. }
  812. if (event.ctrlKey || event.altKey || event.shiftKey) return
  813. for (const keyCode of script.setting.normal.shortcutKeys) {
  814. if (event.keyCode === keyCode) {
  815. for (const module of script.modules) {
  816. if (module.setting && module.shortcutFunc) {
  817. if (module.setting.rewriteShortCutCode) {
  818. if (module.setting.rewriteShortCutCode === event.keyCode) {
  819. module.shortcutFunc[module.setting.key].call(module)
  820. }
  821. } else if (module.setting.shortCutCode === event.keyCode) {
  822. module.shortcutFunc[module.setting.key].call(module)
  823. }
  824. }
  825. if (module.settings) {
  826. for (const setting of module.settings) {
  827. if (module.shortcutFunc) {
  828. if (setting.rewriteShortCutCode) {
  829. if (setting.rewriteShortCutCode === event.keyCode) {
  830. module.shortcutFunc[setting.key].call(module)
  831. }
  832. } else if (setting.shortCutCode === event.keyCode) {
  833. module.shortcutFunc[setting.key].call(module)
  834. }
  835. }
  836. }
  837. }
  838. }
  839. }
  840. }
  841. })
  842. /**
  843. * Bind:Click
  844. * 快捷键编辑面板
  845. */
  846. $('body').on('click', '#hld__shortcut_manage', () => {
  847. if($('#hld__shortcut_panel').length > 0) return
  848. let $shortcutPanel = $(`<div id="hld__shortcut_panel" class="hld__list-panel animated fadeInUp">
  849. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  850. <div>
  851. <div><p><b>编辑快捷键</b></p><div class="hld__float-left"><table class="hld__table hld__table-keyword"><thead><tr><td>功能</td><td width="60">快捷键</td></tr></thead>
  852. <tbody></tbody></table></div><div class="hld__float-left hld__shortcut-desc"><p><b>支持的快捷键范围</b></p><p>键盘 <code>A</code>~<code>Z</code></p><p>左箭头 <code>LEFT</code></p><p>右箭头 <code>RIGHT</code></p><p>上箭头 <code>UP</code></p><p>下箭头 <code>DOWN</code></p><p><i>* 留空则取消快捷键</i></p><br><p>如按键异常请尝试重置按键</p>
  853. </div>
  854. <div class="clearfix"></div></div>
  855. </div>
  856. <div class="hld__btn-groups">
  857. <button class="hld__btn" id="hld__reset_shortcut">重置按键</button>
  858. <button class="hld__btn" id="hld__save_shortcut">保存快捷键</button>
  859. </div>
  860. </div>`)
  861. const insertDom = setting => $shortcutPanel.find('.hld__table tbody').append(`<tr><td>${setting.title || setting.key}</td><td><input type="text" value="${this.getCodeName(setting.rewriteShortCutCode || setting.shortCutCode)}"></td></tr>`)
  862. for (const module of script.modules) {
  863. if (module.setting && module.setting.shortCutCode) {
  864. insertDom(module.setting)
  865. }
  866. if (module.settings) {
  867. for (const setting of module.settings) {
  868. if (setting.shortCutCode) {
  869. insertDom(setting)
  870. }
  871. }
  872. }
  873. }
  874. $('#hld__setting_cover').append($shortcutPanel)
  875. })
  876. /**
  877. * Bind:Click
  878. * 重置快捷键
  879. */
  880. $('body').on('click', '#hld__reset_shortcut', () => {
  881. const defaultShortcut = []
  882. for (const module of script.modules) {
  883. if (module.setting && module.setting.shortCutCode) {
  884. defaultShortcut.push(module.setting.shortCutCode)
  885. }
  886. if (module.settings) {
  887. for (const setting of module.settings) {
  888. setting.shortCutCode && defaultShortcut.push(setting.shortCutCode)
  889. }
  890. }
  891. }
  892. script.setting.normal.shortcutKeys = defaultShortcut
  893. script.saveSetting('重置按键成功,刷新页面生效')
  894. $('#hld__shortcut_panel').remove()
  895. })
  896. /**
  897. * Bind:Click
  898. * 保存快捷键
  899. */
  900. $('body').on('click', '#hld__save_shortcut', () => {
  901. const _this = this
  902. let shortcutKeys = []
  903. $('.hld__table tbody>tr').each(function () {
  904. const v = $(this).find('input').val().trim().toUpperCase()
  905. if (v == '') {
  906. shortcutKeys.push(-1)
  907. } else {
  908. const code = _this.getCodeName(v, 'name')
  909. if (code > 0) shortcutKeys.push(code)
  910. else script.popMsg(`${v}是个无效的快捷键`, 'err')
  911. }
  912. })
  913. if (shortcutKeys.length != script.setting.normal.shortcutKeys.length) return
  914. script.setting.normal.shortcutKeys = shortcutKeys
  915. script.saveSetting('保存按键成功,刷新页面生效')
  916. $('#hld__shortcut_panel').remove()
  917. })
  918. },
  919. getCodeName(val, valType='code') {
  920. const shortcutCode = {
  921. 'A': 65, 'B': 66, 'C': 67, 'D': 68, 'E': 69, 'F': 70, 'G': 71,
  922. 'H': 72, 'I': 73, 'J': 74, 'K': 75, 'L': 76, 'M': 77, 'N': 78,
  923. 'O': 79, 'P': 80, 'Q': 81, 'R': 82, 'S': 83, 'T': 84,
  924. 'U': 85, 'V': 86, 'W': 87, 'X': 88, 'Y': 89, 'Z': 90,
  925. '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57,
  926. 'LEFT': 37, 'RIGHT': 39, 'UP': 38, 'DOWN': 40, '': 0, '': -1
  927. }
  928. if (valType == 'code') {
  929. let keyname = ''
  930. for (let [n, c] of Object.entries(shortcutCode)) {
  931. c == val && (keyname = n)
  932. }
  933. return keyname
  934. } else {
  935. let code = -1
  936. for (let [n, c] of Object.entries(shortcutCode)) {
  937. n == val && (code = c)
  938. }
  939. return code
  940. }
  941. },
  942. style: `
  943. code {padding:2px 4px;font-size:90%;font-weight:bold;color:#c7254e;background-color:#f9f2f4;border-radius:4px;}
  944. .hld__list-panel {position:absolute;top: 100px;left: 50%;background:#fff8e7;padding:15px 20px;border-radius:10px;box-shadow:0 0 10px #666;border:1px solid #591804;z-index:9999;}
  945. .hld__list-panel .hld__list-c {width:45%;}
  946. .hld__list-panel .hld__list-c textarea {box-sizing:border-box;padding:0;margin:0;height:200px;width:100%;resize:none;}
  947. .hld__list-panel .hld__list-desc {margin-top:5px;font-size:9px;color:#666;cursor:help;text-decoration: underline;}
  948. .hld__list-panel .hld__list-c > p:first-child {font-weight:bold;font-size:14px;margin-bottom:10px;}
  949. .hld__table-keyword {margin-top:10px;width:200px;}
  950. .hld__table-keyword tr td:last-child {text-align:center;}
  951. .hld__table-keyword input[type=text] {width:48px;text-transform:uppercase;text-align:center;}
  952. .hld__table{table-layout:fixed;border-top:1px solid #ead5bc;border-left:1px solid #ead5bc}
  953. .hld__table-banlist-buttons{margin-top:10px}
  954. .hld__table thead{background:#591804;border:1px solid #591804;color:#fff}
  955. .hld__table td,.hld__table th{padding:3px 5px;border-bottom:1px solid #ead5bc;border-right:1px solid #ead5bc;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  956. .hld__shortcut-desc {width:120px;margin-left:20px;padding-top:6px}
  957. .hld__shortcut-desc p {margin-bottom:5px;}
  958.  
  959. `
  960. }
  961. /**
  962. * 配置备份模块
  963. * @name BackupModule
  964. * @description 提供配置的导入,导出功能
  965. */
  966. const BackupModule = {
  967. name: 'BackupModule',
  968. title: '配置备份',
  969. backupItems: [],
  970. initFunc() {
  971. /**
  972. * 导入导出设置面板
  973. */
  974. const _this = this
  975. // 在设置面板上添加按钮
  976. script.getModule('SettingPanel').addButton({
  977. id: 'hld__backup_panel',
  978. title: '导入/导出',
  979. desc: '导入/导出配置字符串,包含设置,黑名单,标记名单等等'
  980. })
  981. /**
  982. * Bind:Click
  983. * 导入导出面板
  984. */
  985. $('body').on('click', '#hld__backup_panel', function () {
  986. if($('#hld__export_panel').length > 0) return
  987. $('#hld__setting_cover').append(`
  988. <div id="hld__export_panel" class="hld__list-panel animated fadeInUp">
  989. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  990. <div class="hld__ep-container">
  991. <div>
  992. <p><b>选择导出的设置</b></p>
  993. <div id="hld__export_panel_cb">
  994. <p><label><input type="checkbox" id="hld__cb_export_setting" checked="checked"> 配置</label></p>
  995. </div>
  996. <br>
  997. <p><button id="hld__export__data">导出</button> <button id="hld__import__data">导入</button></p>
  998. </div>
  999. <div>
  1000. <p>
  1001. <b class="hld__help" help="【导出】\n选择要导出的内容,点击导出,复制以下字符串用于备份,分享等\n【导入】\n将字符串复制到以下输入框中,点击导入,将会自动导入字符串中包含的内容">字符串</b>
  1002. <label><input type="checkbox" id="hld__cb_export_encode" checked="checked"> Base64编码</label>
  1003. </p>
  1004. <textarea id="hld__export_str" rows="9"></textarea>
  1005. <p><a href="https://gf.qytechs.cn/zh-CN/scripts?q=NGA%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C%E6%8F%92%E4%BB%B6" target="_blank">使用WebDAV进行配置同步</a></p>
  1006. </div>
  1007. </div>
  1008. <div><p id="hld__export_msg"></p></div>
  1009. </div>
  1010. `)
  1011. // 加载其他模组备份项
  1012. for (const item of _this.backupItems) {
  1013. $('#hld__export_panel_cb').append(`
  1014. <p><label><input type="checkbox" id="hld__cb_export_${item.writeKey}" checked="checked"> ${item.title}</label></p>
  1015. `)
  1016. }
  1017. /**
  1018. * Bind:Click
  1019. * 导出配置
  1020. */
  1021. $('#hld__export__data').click(function(){
  1022. let exportItems = []
  1023. // 基础配置
  1024. if ($('#hld__cb_export_setting').prop('checked')) {
  1025. exportItems.push('setting')
  1026. }
  1027. // 其他模组备份项
  1028. for (const item of _this.backupItems) {
  1029. const $c = $(`#hld__cb_export_${item.writeKey}`)
  1030. if ($c.length > 0 && $c.prop('checked')) {
  1031. exportItems.push(item.writeKey)
  1032. }
  1033. }
  1034. if (Object.keys(exportItems).length == 0) {
  1035. $('#hld__export_msg').html('<span style="color:#CC0000">没有选择任何项目可供导出!</span>')
  1036. return
  1037. }
  1038. const backupB64 = _this.export(exportItems, $('#hld__cb_export_encode').prop('checked'))
  1039. $('#hld__export_str').val(backupB64)
  1040. $('#hld__export_msg').html(`<span style="color:#009900">导出成功(${_this.calculateSize(backupB64.length)}),请复制右侧字符串以备份</span>`)
  1041. })
  1042. /**
  1043. * Bind:Click
  1044. * 导入配置
  1045. */
  1046. $('#hld__import__data').click(function(){
  1047. const dataStr = $('#hld__export_str').val()
  1048. if (dataStr) {
  1049. try {
  1050. const importStatus = _this.import(dataStr, $('#hld__cb_export_encode').prop('checked'))
  1051. importStatus && $('#hld__export_msg').html('<span style="color:#009900">导入成功,刷新浏览器以生效</span>')
  1052. } catch (err){
  1053. script.printLog(`JSON解析失败: ${err}`)
  1054. $('#hld__export_msg').html('<span style="color:#CC0000">字符串有误,解析失败!</span>')
  1055. }
  1056. }
  1057. })
  1058. })
  1059. },
  1060. addItem(item) {
  1061. this.backupItems.push(item)
  1062. },
  1063. // 字符串版本转数字
  1064. vstr2num(str) {
  1065. let num = 0
  1066. str.split('.').forEach((n, i) => num += i < 2 ? +n * 1000 / Math.pow(10, i) : +n)
  1067. return num
  1068. },
  1069. calculateSize(num) {
  1070. if (num == 0) return '0 B'
  1071. let k = 1024
  1072. let sizeStr = ['B','KB','MB','GB']
  1073. let i = 0
  1074. for(let l=0;l<8;l++){
  1075. if(num / Math.pow(k, l) < 1) break
  1076. i = l
  1077. }
  1078. return (num / Math.pow(k, i)).toFixed(2) + ' ' + sizeStr[i]
  1079. },
  1080. export(items, encode=true) {
  1081. const exportData = {
  1082. name: 'NGA-BBS-SCRIPT',
  1083. ver: script.getInfo().version,
  1084. exportDate: new Date().toLocaleString(),
  1085. timestamp: new Date().getTime()
  1086. }
  1087. Array.isArray(items) || (items = [items])
  1088. // 基础配置
  1089. if (items.includes('setting') || items.includes('*')) {
  1090. exportData['setting'] = script.setting.normal
  1091. exportData['advanced_setting'] = script.setting.advanced
  1092. }
  1093. // 其他模组备份项
  1094. for (const item of this.backupItems) {
  1095. if (items.includes(item.writeKey) || items.includes('*')) {
  1096. exportData[item.writeKey] = item.module[item.valueKey]
  1097. }
  1098. }
  1099. const exportDataStr = JSON.stringify(exportData)
  1100. return encode ? this.Base64.encode(exportDataStr) : exportDataStr
  1101. },
  1102. import(dataStr, isEncode=true) {
  1103. dataStr = isEncode ? this.Base64.decode(dataStr) : dataStr
  1104. let obj = JSON.parse(dataStr)
  1105. const unsupported = '3.3.0'
  1106. const currentVer = script.getInfo().version
  1107. const objVer = this.vstr2num(obj.ver)
  1108. if (objVer != 0 && objVer > this.vstr2num(currentVer)) {
  1109. script.popMsg(`此配置是由更高版本(v${obj.ver})的脚本导出,请升级您的脚本 <a title="更新地址" href="https://gf.qytechs.cn/zh-CN/scripts/393991-nga%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C" target="_blank">[脚本地址]</a>`, 'warn')
  1110. return
  1111. }
  1112. if (objVer != 0 && objVer < this.vstr2num(unsupported)) {
  1113. script.popMsg(`此配置是由低版本(v${obj.ver})的脚本导出,当前版本(v${currentVer})已不支持!`, 'err')
  1114. return
  1115. }
  1116. let confirm = window.confirm('此操作会覆盖你的配置,确认吗?')
  1117. if (!confirm) return
  1118. if (Object.keys(obj).includes('setting')) {
  1119. obj.setting && (script.setting.normal = obj.setting)
  1120. obj.advanced_setting && (script.setting.advanced = obj.advanced_setting)
  1121. script.setValue('hld__NGA_setting', JSON.stringify(script.setting.normal))
  1122. script.setValue('hld__NGA_advanced_setting', JSON.stringify(script.setting.advanced))
  1123. }
  1124. // 其他模组备份项
  1125. for (const item of this.backupItems) {
  1126. if (Object.keys(obj).includes(item.writeKey)) {
  1127. item.module[item.valueKey] = obj[item.writeKey]
  1128. script.setValue(`hld__NGA_${item.writeKey}`, JSON.stringify(obj[item.writeKey]))
  1129. }
  1130. }
  1131. script.popMsg('导入成功,刷新页面生效')
  1132. return true
  1133. },
  1134. /**
  1135. * Base64互转
  1136. */
  1137. Base64: {
  1138. encode: (str) => {
  1139. return window.btoa(unescape(encodeURIComponent(str)))
  1140. },
  1141. decode: (str) => {
  1142. return decodeURIComponent(escape(window.atob(str)))
  1143. }
  1144. },
  1145. style: `
  1146. .hld__ep-container{display:flex;width:300px;margin-bottom: 7px;}
  1147. .hld__ep-container p {margin-bottom:10px;}
  1148. .hld__ep-container >div{width:50%;}
  1149. .hld__ep-container textarea {width: 100%;padding:0;margin:0;resize:none;}
  1150. `
  1151. }
  1152. /**
  1153. * 赏面板
  1154. * @name RewardPanel
  1155. * @description 别问,好活当赏
  1156. */
  1157. const RewardPanel = {
  1158. name: 'RewardPanel',
  1159. title: '赏面板',
  1160. initFunc() {
  1161. /**
  1162. * 打赏
  1163. */
  1164. script.getModule('SettingPanel').addButton({
  1165. id: 'hld__reward',
  1166. title: '<span style="margin-right:3px">¥</span>赏',
  1167. desc: '好活当赏'
  1168. })
  1169. /**
  1170. * Bind:Click
  1171. * 打赏面板
  1172. */
  1173. $('body').on('click', '#hld__reward', function () {
  1174. $('#hld__setting_cover').append(`
  1175. <div class="hld__list-panel hld__reward-panel animated fadeInUp">
  1176. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  1177. <div class="hld__reward-info">
  1178. <p><b>喜欢此脚本请可以去作者<a href="${script.getInfo().github}" target="_blank"><b>Github</b></a>点个⭐️</p>
  1179. <p>如果觉得脚本好用<span class="hld__delete-line">摸到鱼了</span>,也可以请作者喝杯☕意思意思,打多少零看缘分😎</p>
  1180. <p>如若有功能需求或者建议,欢迎在社区进行反馈</p>
  1181. </div>
  1182. <div class="hld__flex">
  1183. <div class="hld__list-c"><img src="${IMG_REWARD_ALIPAY}"></div>
  1184. <div class="hld__list-c"><img src="${IMG_REWARD_WXPAY}"></div>
  1185. </div>
  1186. <div class="hld__source">
  1187. <a href="${script.getInfo().github}" target="_blank"><img alt="Mozilla Add-on" src="https://img.shields.io/github/stars/kisshang1993/NGA-BBS-Script?label=Star&style=social"></a>
  1188. <a href="${script.getInfo().update}" target="_blank"><img alt="Mozilla Add-on" src="https://img.shields.io/badge/Greasy%20Fork-NGA优化摸鱼体验-brightgreen"></a>
  1189. </div>
  1190. </div>
  1191. `)
  1192. })
  1193. },
  1194. style: `
  1195. .hld__reward-panel {width:500px;}
  1196. .hld__reward-panel .hld__reward-info {display:block;font-size:15px;margin-bottom:20px;line-height:20px;}
  1197. .hld__reward-panel .hld__reward-info p {margin-bottom:5px;}
  1198. .hld__delete-line {text-decoration:line-through;color:#666;}
  1199. .hld__reward-panel .hld__list-c {width:50%;}
  1200. .hld__reward-panel .hld__list-c:first-child {margin-right:15px;}
  1201. .hld__reward-panel .hld__list-c>img {width:100%;height:auto;}
  1202. .hld__reward-panel .hld__source {margin-top:15px;}
  1203. .hld__reward-panel .hld__source > a {margin-right:10px;}
  1204. `
  1205. }
  1206. /**
  1207. * 隐藏头像模块
  1208. * @name HideAvatar
  1209. * @description 此模块提供了可以快捷键切换显示隐藏头像
  1210. */
  1211. const HideAvatar = {
  1212. name: 'HideAvatar',
  1213. title: '隐藏头像',
  1214. setting: {
  1215. shortCutCode: 81, // Q
  1216. type: 'normal',
  1217. key: 'hideAvatar',
  1218. default: true,
  1219. title: '隐藏头像',
  1220. menu: 'left'
  1221. },
  1222. renderFormsFunc($el) {
  1223. if (script.setting.normal.hideAvatar) {
  1224. $el.find('.avatar, .avatar+img').css('display', 'none')
  1225. $el.find('.c1').css('background-image', 'none')
  1226. }
  1227. },
  1228. shortcutFunc: {
  1229. hideAvatar() {
  1230. if (script.setting.normal.hideAvatar || script.setting.advanced.dynamicEnable) {
  1231. $('.avatar, .avatar+img').toggle()
  1232. script.popNotification(`${$('.avatar:hidden').length == 0 ? '显示' : '隐藏'}头像`)
  1233. }
  1234. }
  1235. },
  1236. asyncStyle() {
  1237. return `
  1238. .posterinfo .avatar+img {display:${script.setting.normal.hideAvatar ? 'none' : 'inline'};}
  1239. `
  1240. }
  1241. }
  1242. /**
  1243. * 隐藏头像模块
  1244. * @name HideSmile
  1245. * @description 此模块提供了可以快捷键切换显示隐藏表情
  1246. * 其中隐藏的表情会用文字来替代
  1247. */
  1248. const HideSmile = {
  1249. name: 'HideSmile',
  1250. title: '隐藏头像',
  1251. setting: {
  1252. shortCutCode: 87, // W
  1253. type: 'normal',
  1254. key: 'hideSmile',
  1255. default: false,
  1256. title: '隐藏表情',
  1257. menu: 'left'
  1258. },
  1259. renderFormsFunc($el) {
  1260. $el.find('.c2 img').each(function () {
  1261. const classs = $(this).attr('class')
  1262. if (classs && classs.includes('smile') && !$(this).is(':hidden')) {
  1263. const alt = $(this).attr('alt')
  1264. const $alt = $('<span class="smile_alt_text">[' + alt + ']</span>')
  1265. script.setting.normal.hideSmile ? $(this).hide() : $alt.hide()
  1266. $(this).after($alt)
  1267. }
  1268. })
  1269. },
  1270. shortcutFunc: {
  1271. hideSmile() {
  1272. if (script.setting.normal.hideSmile || script.setting.advanced.dynamicEnable) {
  1273. $('.c2 img').each(function () {
  1274. const classs = $(this).attr('class');
  1275. if (classs && classs.includes('smile')) $(this).toggle()
  1276. })
  1277. $('.smile_alt_text').toggle()
  1278. script.popNotification(`${$('.smile_alt_text:hidden').length > 0 ? '显示' : '隐藏'}表情`)
  1279. }
  1280. }
  1281. }
  1282. }
  1283. /**
  1284. * 贴内图片缩放模块
  1285. * @name ImgResize
  1286. * @description 此模块提供了可以调整贴内图片的尺寸
  1287. */
  1288. const ImgResize = {
  1289. name: 'ImgResize',
  1290. title: '贴内图片缩放',
  1291. settings: [{
  1292. type: 'normal',
  1293. key: 'imgResize',
  1294. title: '贴内图片缩放',
  1295. default: true,
  1296. menu: 'left'
  1297. }, {
  1298. type: 'advanced',
  1299. key: 'imgResizeWidth',
  1300. default: 200,
  1301. title: '图片缩放宽度',
  1302. desc: '贴内图片缩放的宽度,高度自适应,单位px',
  1303. menu: 'left'
  1304. }],
  1305. renderFormsFunc($el) {
  1306. $el.find('.c2 img').each(function () {
  1307. const classs = $(this).attr('class')
  1308. if ((!classs || !classs.includes('smile')) && script.setting.normal.imgResize) {
  1309. $(this).addClass('hld__img-resize').attr('hld-img-resize', 'ok').attr('title', '点击大图显示')
  1310. }
  1311. })
  1312. },
  1313. asyncStyle: () => {
  1314. return `
  1315. .hld__img-resize {outline:none !important;outline-offset:'';cursor:alias;min-width:auto !important;min-height:auto !important;max-width:${script.setting.advanced.imgResizeWidth || 200}px !important;max-height:none !important;margin:5px;}
  1316. `
  1317. }
  1318. }
  1319. /**
  1320. * 隐藏图片模块
  1321. * @name HideImage
  1322. * @description 此模块提供了可以快捷键切换显示隐藏图片
  1323. * 其中隐藏的图片会用一个按钮来替代
  1324. */
  1325. const HideImage = {
  1326. name: 'HideImage',
  1327. title: '隐藏图片',
  1328. setting: {
  1329. shortCutCode: 69, // E
  1330. type: 'normal',
  1331. key: 'hideImage',
  1332. default: false,
  1333. title: '隐藏贴内图片',
  1334. menu: 'left'
  1335. },
  1336. renderFormsFunc($el) {
  1337. $el.find('.c2 img').each(function () {
  1338. const classs = $(this).attr('class')
  1339. if ((!classs || !classs.includes('smile')) && !$(this).is(':hidden')) {
  1340. $(this).addClass('hld__img-postimg')
  1341. // 显示原图
  1342. $(this).attr('src', $(this).attr('src').replace('.medium.jpg', '')).attr('hld-hideimg', 'ok')
  1343. let $imgB = $('<button class="switch-img" style="display:none">图</button>')
  1344. $imgB.on('click', function () {
  1345. $(this).prev('img').toggle()
  1346. $(this).text($(this).prev('img').is(':hidden') ? '图' : '隐藏')
  1347. })
  1348. if (script.setting.normal.hideImage) {
  1349. $(this).hide();
  1350. $imgB.show()
  1351. }
  1352. $(this).after($imgB)
  1353. }
  1354. })
  1355. },
  1356. shortcutFunc: {
  1357. hideImage() {
  1358. if (!script.setting.advanced.dynamicEnable) return
  1359. if ($('.hld__img-postimg:hidden').length < $('.switch-img').length) {
  1360. $('.hld__img-postimg').hide()
  1361. $('.switch-img').text('图').show()
  1362. script.popNotification(`隐藏图片`)
  1363. return
  1364. }
  1365. $('.hld__img-postimg').each(function () {
  1366. $(this).toggle()
  1367. $(this).is(':hidden') ? $(this).next('button.switch-img').show() : $(this).next('button.switch-img').hide()
  1368. })
  1369. script.popNotification(`${$('.switch-img:hidden').length > 0 ? '显示' : '隐藏'}图片`)
  1370. }
  1371. }
  1372. }
  1373. /**
  1374. * 隐藏签名模块
  1375. * @name HideSign
  1376. * @description 此模块提供了可以配置默认隐藏签名
  1377. */
  1378. const HideSign = {
  1379. name: 'HideSign',
  1380. title: '隐藏签名',
  1381. setting: {
  1382. type: 'normal',
  1383. key: 'hideSign',
  1384. default: true,
  1385. title: '隐藏签名',
  1386. menu: 'left'
  1387. },
  1388. renderFormsFunc($el) {
  1389. script.setting.normal.hideSign && $el.find('.sign, .sigline').css('display', 'none')
  1390. }
  1391. }
  1392. /**
  1393. * 隐藏图片模块
  1394. * @name HideHeader
  1395. * @description 此模块提供了可以配置默认隐藏版头
  1396. * 以及一个高级配置可选一起隐藏顶部背景
  1397. */
  1398. const HideHeader = {
  1399. name: 'HideHeader',
  1400. title: '隐藏图片',
  1401. settings: [{
  1402. type: 'normal',
  1403. key: 'hideHeader',
  1404. default: true,
  1405. title: '隐藏版头/版规/子版入口',
  1406. menu: 'left'
  1407. }, {
  1408. type: 'advanced',
  1409. key: 'hideCustomBg',
  1410. default: true,
  1411. title: '隐藏背景图片',
  1412. desc: '选中时: 隐藏版头的同时顶部背景图片\n取消时: 无操作',
  1413. menu: 'right'
  1414. }],
  1415. renderAlwaysFunc($el) {
  1416. //隐藏版头
  1417. if (script.setting.normal.hideHeader && $('#hld__switch_header').length == 0) {
  1418. $('#toppedtopic').hide()
  1419. $('#toppedtopic').length > 0 && $('#sub_forums').hide()
  1420. let $toggleHeaderBtn = $('<button style="position: absolute;right: 16px;" id="hld__switch_header">切换显示版头</button>')
  1421. $toggleHeaderBtn.click(() => $('#toppedtopic, #sub_forums').toggle())
  1422. $('#toptopics > div > h3').append($toggleHeaderBtn)
  1423. }
  1424. if(script.setting.normal.hideHeader && script.setting.advanced.hideCustomBg) {
  1425. $('#custombg').hide()
  1426. $('#mainmenu').css('margin', '0px')
  1427. }
  1428. },
  1429. style: `
  1430. #m_threads .toptopicsRight {float:none;width:auto;}
  1431. .topicrowsLeftC {margin-right:0;}
  1432. `
  1433. }
  1434. /**
  1435. * Excel模块
  1436. * @name ExcelMode
  1437. * @description 此模块提供了可以快捷键切换Excel模式
  1438. * 以及一个高级配置可选更改Excel左侧序号的类型
  1439. */
  1440. const ExcelMode = {
  1441. name: 'ExcelMode',
  1442. title: 'Excel模式',
  1443. settings: [{
  1444. shortCutCode: 82, // R
  1445. type: 'normal',
  1446. key: 'excelMode',
  1447. default: false,
  1448. title: 'Excel模式',
  1449. menu: 'left'
  1450. }, {
  1451. type: 'advanced',
  1452. key: 'excelTheme',
  1453. default: 'tencent',
  1454. options: [{
  1455. label: '腾讯文档',
  1456. value: 'tencent'
  1457. }, {
  1458. label: 'WPS',
  1459. value: 'wps'
  1460. }, {
  1461. label: 'Office',
  1462. value: 'office'
  1463. }],
  1464. title: 'Excel皮肤',
  1465. desc: 'Excel的皮肤\n腾云文档是矢量图形绘制,适应各种分辨率,不会失真,推荐优先使用\nWPS与Office为图片拼接而成,分辨率为1080P,高于此分辨率可能会失真',
  1466. menu: 'left'
  1467. }, {
  1468. type: 'advanced',
  1469. key: 'excelNoMode',
  1470. default: false,
  1471. title: 'Excel左列序号',
  1472. desc: 'Excel最左列的显示序号,此策略为尽可能的更像Excel\n选中时: Excel最左栏为从1开始往下,逐行+1\n取消时: Excel最左栏为原始的回帖数\n*此功能仅在贴列表有效',
  1473. menu: 'left'
  1474. }, {
  1475. type: 'advanced',
  1476. key: 'excelTitle',
  1477. default: '工作簿1',
  1478. title: 'Excel覆盖标题',
  1479. desc: 'Excel模式下标签栏的名称, 如留空, 则显示原始标题',
  1480. menu: 'left'
  1481. }],
  1482. beforeUrl: window.location.href,
  1483. initFunc() {
  1484. // 生成列标题字母列表
  1485. const columnLetters = () => {
  1486. let capital = []
  1487. let columnLetters = []
  1488. for (let i=65;i<91;i++) capital.push(String.fromCharCode(i))
  1489. Array('', 'A', 'B', 'C').forEach(n => capital.forEach(c => columnLetters.push(`${n}${c}`)))
  1490. return columnLetters
  1491. }
  1492. if (script.setting.advanced.excelTheme == 'tencent') {
  1493. // 腾讯文档元素
  1494. // 插入Excel头部
  1495. $('body').append(`
  1496. <div class="hld__excel-div hld__excel-header">
  1497. <div class="hld__excel-titlebar">
  1498. <div class="hld__excel-titlebar-content hld__excel-icon24" style="margin:2px 2px 2px 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_1')});"></div>
  1499. <div class="hld__excel-titlebar-content hld__excel-icon12" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1500. <div style="height: 24px;border-right: 1px solid rgb(0, 0, 0);opacity: 0.06;margin: 0 12px;vertical-align: middle;"></div>
  1501. <div class="hld__excel-titlebar-title"></div>
  1502. <div class="hld__excel-titlebar-content hld__excel-icon16" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_3')});"></div>
  1503. <div class="hld__excel-titlebar-content hld__excel-icon16" style="margin-left: 12px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_4')});"></div>
  1504. <div class="hld__excel-titlebar-content hld__excel-icon16" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_5')});"></div>
  1505. <div style="margin-left: 5px;font-size: 12px;line-height: 20px;height: 18px;;color: #000;opacity: 0.48;font-weight:400;">上次修改是在2小时前进行的</div>
  1506. <div style="flex-grow: 1;"></div>
  1507. <div style="height: 24px;border-right: 1px solid rgb(0, 0, 0);opacity: 0.06;margin: 0 12px;vertical-align: middle;"></div>
  1508. <div style="width:28px;height:28px;border-radius: 4px;background: #e9e9e9;text-align: center;line-height: 32px;">🐟︎</div>
  1509. </div>
  1510. <div class="hld__excel-toolbar">
  1511. ${Array.from({length: 4}, (_, i) => '<div class="hld__excel-titlebar-content hld__excel-icon20" style="margin:0 6px;background-image:url(' + getExcelTheme(script.setting.advanced.excelTheme, "icon_"+(10+i)) + ');"></div>').join('')}
  1512. <div style="height: 16px;border-right: 1px solid rgb(0, 0, 0);opacity: 0.06;margin: 0 4px;vertical-align: middle;"></div>
  1513. <div class="hld__excel-titlebar-content hld__excel-icon20" style="margin-left: 8px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_14')});"></div>
  1514. <div style="padding: 0 2px;">插入</div>
  1515. <div class="hld__excel-titlebar-content hld__excel-icon12" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1516. <div style="height: 16px;border-right: 1px solid rgb(0, 0, 0);opacity: 0.06;margin: 0 8px;vertical-align: middle;"></div>
  1517. <div style="padding: 0 30px 0 4px;">常规</div>
  1518. <div class="hld__excel-titlebar-content hld__excel-icon12" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1519. <div class="hld__excel-titlebar-content hld__excel-icon20" style="margin-left: 12px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_15')});"></div>
  1520. <div style="margin-left: 1px;">
  1521. <div class="hld__excel-titlebar-content hld__excel-icon12" style="transform: rotate(180deg);background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1522. <div class="hld__excel-titlebar-content hld__excel-icon12" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1523. </div>
  1524. <div style="height: 16px;border-right: 1px solid #000;opacity: 0.06;margin: 0 4px;vertical-align: middle;"></div>
  1525. <div style="padding: 0 4px 0 16px;">默认字体</div>
  1526. <div class="hld__excel-titlebar-content hld__excel-icon12" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1527. <div style="padding: 0 4px 0 13px;">10</div>
  1528. <div class="hld__excel-titlebar-content hld__excel-icon12" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1529. <div class="hld__excel-titlebar-content hld__excel-icon20" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_16')});"></div>
  1530. <div class="hld__excel-titlebar-pick">
  1531. <div class="hld__excel-titlebar-content hld__excel-icon20" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_17')});"></div>
  1532. <div class="hld__excel-titlebar-indication" style="background-color: #000;"></div>
  1533. </div>
  1534. <div class="hld__excel-titlebar-content hld__excel-icon12" style="margin-left: 4px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1535. <div class="hld__excel-titlebar-pick">
  1536. <div class="hld__excel-titlebar-content hld__excel-icon20" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_18')});"></div>
  1537. <div class="hld__excel-titlebar-indication" style="background-color: #8cddfa;"></div>
  1538. </div>
  1539. <div class="hld__excel-titlebar-content hld__excel-icon12" style="margin-left: 4px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1540. <div class="hld__excel-titlebar-content hld__excel-icon20" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_19')});"></div>
  1541. <div class="hld__excel-titlebar-content hld__excel-icon12" style="margin-left: 2px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1542. <div class="hld__excel-titlebar-content hld__excel-icon20" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_20')});"></div>
  1543. <div style="height: 16px;border-right: 1px solid #000;opacity: 0.06;margin: 0 10px;vertical-align: middle;"></div>
  1544. ${Array.from({length: 4}, (_, i) => '<div class="hld__excel-titlebar-content hld__excel-icon20" style="background-image:url(' + getExcelTheme(script.setting.advanced.excelTheme, "icon_"+(21+i)) + ');"></div><div class="hld__excel-titlebar-content hld__excel-icon12" style="margin-left: 2px;margin-right: '+ (i==3?'0':'10') +'px;background-image:url(' + getExcelTheme(script.setting.advanced.excelTheme, "icon_2") + ');"></div>').join('')}
  1545. <div style="height: 16px;border-right: 1px solid #000;opacity: 0.06;margin: 0 10px;vertical-align: middle;"></div>
  1546. <div class="hld__excel-titlebar-content hld__excel-icon20" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_25')});"></div>
  1547. <div class="hld__excel-titlebar-content hld__excel-icon12" style="margin-left: 4px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1548. <div style="height: 16px;border-right: 1px solid #000;opacity: 0.06;margin: 0 10px;vertical-align: middle;"></div>
  1549. ${Array.from({length: 4}, (_, i) => '<div class="hld__excel-titlebar-content hld__excel-icon20" style="background-image:url(' + getExcelTheme(script.setting.advanced.excelTheme, "icon_"+(26+i)) + ');"></div><div class="hld__excel-titlebar-content hld__excel-icon12" style="margin-left: 2px;margin-right: '+ (i==3?'0':'10') +'px;background-image:url(' + getExcelTheme(script.setting.advanced.excelTheme, "icon_2") + ');"></div>').join('')}
  1550. <div style="height: 16px;border-right: 1px solid #000;opacity: 0.06;margin: 0 10px;vertical-align: middle;"></div>
  1551. <div class="hld__excel-titlebar-content hld__excel-icon20" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_20')});"></div>
  1552. <div style="flex-grow: 1;"></div>
  1553. </div>
  1554. <div class="hld__excel-formulabar">
  1555. <div style="border-right: 1px solid #e0e2e4;color: #777;text-align: center;width: 50px;font-size: 12px;height: 25px;line-height: 25px;font-weight:400;">A1</div>
  1556. </div>
  1557. <div class="hld__excel-h4">
  1558. <div class="hld__excel-sub"><div></div></div>
  1559. ${(columnLetters().map(c => '<div class="hld__excel-column">'+c+'</div>')).join('')}
  1560. </div>
  1561. </div>
  1562. `)
  1563. // 插入Excel尾部
  1564. $('body').append(`
  1565. <div class="hld__excel-div hld__excel-footer">
  1566. <div class="hld__excel-icon24" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_33')});"></div>
  1567. <div class="hld__excel-icon24" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_34')});"></div>
  1568. <div class="hld__excel-sheet-tab">
  1569. <div class="hld__excel-sheet-name">
  1570. <div>工作表1</div>
  1571. <div class="hld__excel-icon12" style="margin-left: 4px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1572. </div>
  1573. <div class="hld__excel-sheet-underblock"></div>
  1574. </div>
  1575. <div style="flex-grow: 1;"></div>
  1576. <div class="hld__excel-icon24" style="margin-left: 10px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_35')});"></div>
  1577. <div class="hld__excel-icon12" style="margin-left: 2px;background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_2')});"></div>
  1578. <div style="height: 16px;border-right: 1px solid #000;opacity: 0.12;margin: 0 10px;vertical-align: middle;"></div>
  1579. <div class="hld__excel-icon24" style="background-image:url(${getExcelTheme(script.setting.advanced.excelTheme, 'icon_36')});"></div>
  1580. <div class="hld__excel-footer-item" style="font-size: 20px;margin-left:20px;">-</div>
  1581. <div class="hld__excel-footer-item" style="font-weight: 400">100%</div>
  1582. <div class="hld__excel-footer-item" style="font-size: 20px;">+</div>
  1583. <div style="width:10px;"></div>
  1584. </div>
  1585. `)
  1586. } else {
  1587. // WPS与Office元素
  1588. // 插入Excel头部
  1589. $('body').append(`
  1590. <div class="hld__excel-div hld__excel-header">
  1591. <div class="hld__excel-h1">
  1592. <div class="hld__excel-title">${script.setting.advanced.excelTitle || document.title} - Excel</div>
  1593. <img class="hld__excel-img-h1-l1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'H_L_1')}">
  1594. <img class="hld__excel-img-h1-r1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'H_R_1')}">
  1595. </div>
  1596. <div class="hld__excel-h2">
  1597. <img class="hld__excel-img-h2-l1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'H_L_2')}">
  1598. <img class="hld__excel-img-h2-r1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'H_R_2')}">
  1599. </div>
  1600. <div class="hld__excel-h3">
  1601. <img class="hld__excel-img-h3-l1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'H_L_3')}">
  1602. <img class="hld__excel-img-h3-r1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'H_R_3')}">
  1603. <div class="hld__excel-fx"></div>
  1604. </div>
  1605. <div class="hld__excel-h4">
  1606. <div class="hld__excel-sub"><div></div></div>
  1607. ${(columnLetters().map(c => '<div class="hld__excel-column">'+c+'</div>')).join('')}
  1608. </div>
  1609. </div>
  1610. `)
  1611. // 插入Excel尾部
  1612. $('body').append(`
  1613. <div class="hld__excel-div hld__excel-footer">
  1614. <div class="hld__excel-f1">
  1615. <img class="hld__excel-img-f1-l1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'F_L_1')}">
  1616. <img class="hld__excel-img-f1-r1" src="${getExcelTheme(script.setting.advanced.excelTheme, 'F_R_1')}">
  1617. </div>
  1618. <div class="hld__excel-f2">
  1619. <img class="hld__excel-img-fl2" src="${getExcelTheme(script.setting.advanced.excelTheme, 'F_L_2')}">
  1620. <img class="hld__excel-img-fr2" src="${getExcelTheme(script.setting.advanced.excelTheme, 'F_R_2')}">
  1621. </div>
  1622. </div>
  1623. `)
  1624. }
  1625.  
  1626. $('#hld__excel_setting').click(()=>$('#hld__setting_cover').css('display', 'block'))
  1627. $('#mainmenu .half').parent().append($('#mainmenu .half').clone(true).addClass('hld__half-clone').text($('#mainmenu .half').text().replace('你好', '')))
  1628. if(script.setting.normal.excelMode) {
  1629. if(this.beforeUrl.includes('thread.php') || this.beforeUrl.includes('read.php')) {
  1630. this.switchExcelMode()
  1631. }
  1632. }
  1633. },
  1634. renderAlwaysFunc($el) {
  1635. $('.hld__excel-theme-' + script.setting.advanced.excelTheme).length == 0 && $('body').addClass('hld__excel-theme-' + script.setting.advanced.excelTheme)
  1636. if(script.setting.normal.excelMode && window.location.href != this.beforeUrl) {
  1637. this.beforeUrl = window.location.href
  1638. if(this.beforeUrl.includes('thread.php') || this.beforeUrl.includes('read.php')) {
  1639. $('.hld__excel-body').length == 0 && $('body').addClass('hld__excel-body')
  1640. }else {
  1641. $('.hld__excel-body').length > 0 && $('body').removeClass('hld__excel-body')
  1642. }
  1643. $('body').toggleClass('hld__excel-original-no', !script.setting.advanced.excelNoMode)
  1644. }
  1645. if(script.setting.normal.excelMode && $('.hld__excel-body').length > 0 && $('#mmc').length == 0) {
  1646. $('body').addClass('hld__excel-body-err')
  1647. }else {
  1648. $('body').removeClass('hld__excel-body-err')
  1649. }
  1650. // Excel Title
  1651. if ($('.hld__excel-body').length > 0) {
  1652. const excelTitle = script.setting.advanced.excelTitle
  1653. if (excelTitle) {
  1654. $(document).attr('title') != excelTitle && $(document).attr('title', excelTitle)
  1655. }
  1656. $('.hld__excel-titlebar-title').html(excelTitle || $(document).attr('title'))
  1657. $('#hld__excel_icon').length == 0 && $('head').append(`<link id= "hld__excel_icon" rel="shortcut icon" type="image/png" href="${IMG_EXCEL_ICON}" />`)
  1658. }
  1659. },
  1660. renderFormsFunc($el) {
  1661. $el.find('.postrow>td:first-child').before('<td class="c0"></td>')
  1662. },
  1663. shortcutFunc: {
  1664. excelMode() {
  1665. if (script.setting.normal.excelMode || script.setting.advanced.dynamicEnable) {
  1666. this.switchExcelMode()
  1667. script.popNotification($('.hld__excel-body').length > 0 ? 'Excel模式' : '普通模式')
  1668. }
  1669. }
  1670. },
  1671. /**
  1672. * 切换Excel模式
  1673. * @method switchExcelMode
  1674. */
  1675. switchExcelMode: () => {
  1676. $('body').toggleClass('hld__excel-body')
  1677. !script.setting.advanced.excelNoMode && $('body').addClass('hld__excel-original-no')
  1678. script.setting.normal.darkMode && script.popMsg('Excel模式与暗黑模式不兼容, 请勿重合使用', 'warn')
  1679. },
  1680. style: `
  1681. /* WPS风格 */
  1682. .hld__excel-body-err {padding-top: 200px}
  1683. .hld__excel-header, .hld__excel-footer, .hld__excel-setting, .hld__half-clone {display: none;}
  1684. .hld__excel-header>div, .hld__excel-footer>div {position: relative;box-sizing: border-box;}
  1685. .hld__excel-header img, .hld__excel-footer img {position: absolute;}
  1686. .hld__excel-header {border-bottom:1px solid #bbbbbb;}
  1687. .hld__excel-title {display:none;}
  1688. .hld__excel-h1 {height:30px;background:#f3f5f8;border-bottom:1px solid #c5cbd6;}
  1689. .hld__excel-h2 {height:102px;background:#f4f4f4;}
  1690. .hld__excel-img-h1-l1, .hld__excel-img-h2-l1, .hld__excel-img-f1-l1, .hld__excel-img-fl2 {top:0;left:0;}
  1691. .hld__excel-img-h1-r1, .hld__excel-img-h2-r1, .hld__excel-img-f1-r1, .hld__excel-img-fr2 {top:0;right:0;}
  1692. .hld__excel-h3 {height:44px;background:#e8e8e8;box-shadow: inset 0 3px 5px #d9d9d9;}
  1693. .hld__excel-img-h3-l1 {top:12px;left:0;}
  1694. .hld__excel-img-h3-r1 {toP:8px;right:0;}
  1695. .hld__excel-fx {position: absolute;top:12px;left:253px;right:45px;height:24px;box-sizing: border-box;border:1px solid #cccccc;border-radius:4px;background:#ffffff;}
  1696. .hld__excel-h4 {height:21px;display:flex;overflow: hidden;}
  1697. .hld__excel-h4 > div {height:21px;border-right:1px solid #c8c8c8;box-sizing:border-box;flex-shrink: 0;}
  1698. .hld__excel-sub {width:34px;position: relative;}
  1699. .hld__excel-sub > div {position: absolute;right:4px;bottom:4px;width: 0px;height: 0px;border-top: 6px solid transparent;border-left: 6px solid transparent;border-right: 6px solid #b8b8b8;border-bottom: 6px solid #b8b8b8;}
  1700. .hld__excel-column {width: 72px;line-height:21px;text-align:center;color:#444444;font-family: sans-serif;font-weight:100;font-size:14px;}
  1701. .hld__excel-f1 {height:22px;background:#e8e8e8;}
  1702. .hld__excel-f2 {height:28px;background:#f4f4f4;}
  1703. .hld__excel-body {background:#fff !important;}
  1704. .hld__excel-body #mainmenu {position: fixed;top: 5px;right: 75px;width: 425px;z-index: 98;}
  1705. .hld__excel-body #mainmenu .right {float:none;}
  1706. .hld__excel-body #mainmenu .stdbtn {background:none;box-shadow:none;}
  1707. .hld__excel-body #mainmenu .half {display:none;}
  1708. .hld__excel-body #mainmenu .hld__half-clone {display:block;width: 150px;text-align: right;overflow: hidden;text-overflow:ellipsis;white-space: nowrap;}
  1709. .hld__excel-body #mainmenu .half {color:#f4f4f4 !important;}
  1710. .hld__excel-body #mainmenu .stdbtn a:hover {background:none;text-decoration:underline;color:#2c5787 !important;}
  1711. .hld__excel-body #mainmenu .mmdefault.cell input {padding:0;margin:0;background:#ededed;border:1px solid #c9d0dc;border-radius:10px;box-shadow:none;font-size:13px !important;}
  1712. .hld__excel-body #mainmenu, .hld__excel-body #mainmenu .half, .hld__excel-body #mainmenu td a, .hld__excel-body #mainmenu .stdbtn .innerbg, .hld__excel-body #mainmenu, .hld__excel-body #mainmenu .stdbtn a, .hld__excel-body #mainmenu .stdbtn .td {height: 20px !important;line-height: 20px !important;padding: 0 5px !important;background:none;color:#424242 !important;}
  1713. .hld__excel-body #mainmenu .innerbg > div:nth-child(2) > div:first-child {display:none;}
  1714. .hld__excel-body .single_ttip2 {position: fixed !important;z-index:999 !important;top:30px !important;border-color:#888;}
  1715. .hld__excel-body .hld__excel-body #mainmenu, .hld__excel-body .catenew,.hld__excel-body #toptopics,.hld__excel-body #m_pbtntop,.hld__excel-body #m_fopts,.hld__excel-body #b_nav,.hld__excel-body #fast_post_c,.hld__excel-body #custombg,.hld__excel-body #m_threads th,.hld__excel-body #m_posts th,.hld__excel-body .r_container,.hld__excel-body #footer,.hld__excel-body .clickextend {display:none !important;}
  1716. .hld__excel-body #mmc {margin-top:195px;margin-bottom:35px;}
  1717. .hld__excel-body .postBtnPos > div, .hld__excel-body .postBtnPos .stdbtn a {background:#fff !important;border-color:#bbb;}
  1718. .hld__excel-body .hld__excel-div,.hld__excel-body .hld__excel-setting {display:block;}
  1719. .hld__excel-body .hld__excel-setting {position:fixed;width:60px;height:20px;top:5px;right:95px;background:#f2f4f7;z-index:999;}
  1720. .hld__excel-body .hld__excel-setting img {width:20px;height:auto;vertical-align:middle;}
  1721. .hld__excel-body .hld__excel-setting a {margin-left:5px;vertical-align:middle;}
  1722. .hld__excel-body .hld__excel-header {position:fixed;top:0;left:0;height:196px;}
  1723. .hld__excel-body .hld__excel-footer {position:fixed;bottom:0;left:0;height:50px;}
  1724. .hld__excel-body .hld__excel-header, .hld__excel-body .hld__excel-footer {width: 100%;text-align: center;font-size: 16px;font-weight: bold;background:#e8e8e8;color:#337ab7;line-height: 45px;}
  1725. .hld__excel-body .hld__excel-header>img, .hld__excel-body .hld__excel-footer>img{position:absolute;top:0;left:0}
  1726. .hld__excel-body #m_nav {position:fixed;top:136px;left:261px;margin:0;padding:0;z-index:99;width: 9999px;}
  1727. .hld__excel-body #m_nav .nav_spr {display:block;border:0;border-radius:0;padding:0;box-shadow:none;background:none;margin-top: 18px;margin-left: 10px;}
  1728. .hld__excel-body #m_nav .nav_spr span {color:#000;font-size:16px;vertical-align:unset;font-weight:normal;}
  1729. .hld__excel-body #m_nav .nav_root,.hld__excel-body #m_nav .nav_link {background:none;border:none;box-shadow:none;padding:0;color:#000;border-radius:0;font-weight:normal;}
  1730. .hld__excel-body .nav {font-size:14px !important;}
  1731. .hld__excel-body #mainmenu .stdbtn a {font-size:13px !important;}
  1732. .hld__excel-body #m_threads {margin:0;}
  1733. .hld__excel-body .postBtnPos > div {z-index:9991;}
  1734. .hld__excel-body #topicrows {border:none;box-shadow:none;border-radius:0;margin:0;background-color:#fff;counter-reset:num;border-spacing:0;}
  1735. .hld__excel-body #topicrows tbody {border-spacing:0;}
  1736. .hld__excel-body .topicrow {border-spacing:0;}
  1737. .hld__excel-body #topicrows td {background:#fff;padding:5px 0;margin:0;border:none;border-right:1px solid #bbbbbb;border-bottom:1px solid #bbbbbb;margin-right:-1px;}
  1738. .hld__excel-body .topicrow .c1 {width:33px;background:#e8e8e8 !important;}
  1739. .hld__excel-body .topicrow .c1 a {display:none;color: #777777 !important;font-size: 16px !important;font-family: auto;}
  1740. .hld__excel-body.hld__excel-original-no .topicrow .c1:before {display:none;}
  1741. .hld__excel-body.hld__excel-original-no .topicrow .c1 a {display:inline-block;}
  1742. .hld__excel-body.hld__excel-original-no .topicrow .c1 img {width:20px;}
  1743. .hld__excel-body .topicrow .c1:before {content:counter(num);counter-increment:num;color:#777777;font-size:16px;}
  1744. .hld__excel-body .topicrow .c2 {padding-left:5px !important;}
  1745. .hld__excel-body .topicrow .c3 {color:#1a3959 !important;}
  1746. .hld__excel-body .topicrow .c3 > div, .hld__excel-body .topicrow .c4 > div {background:#FFF !important;}
  1747. .hld__excel-body .topicrow .c3 > div a, .hld__excel-body .topicrow .c4 > div a {color:#888 !important;}
  1748. .hld__excel-body .block_txt {background:#fff !important;color:#1a3959 !important;border-radius:0;padding:0 !important;min-width:0 !important;font-weight:normal;}
  1749. .hld__excel-body .quote {background:#fff !important;}
  1750. .hld__excel-body #m_posts .block_txt {font-weight:bold;}
  1751. .hld__excel-body .topicrow .postdate,.hld__excel-body .topicrow .replydate {display:inline;margin:10px;}
  1752. .hld__excel-body #m_pbtnbtm {margin:0;border-bottom:1px solid #bbbbbb;}
  1753. .hld__excel-body .hld__country-flag {border:.5px solid rgba(0,0,0,.2);}
  1754. .hld__excel-body #pagebbtm,.hld__excel-body #m_pbtnbtm .right_ {margin:0;}
  1755. .hld__excel-body #pagebbtm:before {display:block;line-height:35px;width:33px;float:left;content:"#";border-right:1px solid #bbbbbb;color:#777;font-size:16px;background:#e8e8e8;}
  1756. .hld__excel-body #m_pbtnbtm td {line-height:35px;padding:0 5px;}
  1757. .hld__excel-body #m_pbtnbtm .stdbtn {box-shadow:none;border:none !important;padding:0;padding-left:5px;background:#fff;border-radius:0;font-size:13px !important;}
  1758. .hld__excel-body #m_pbtnbtm .stdbtn .invert {color:#591804;}
  1759. .hld__excel-body #m_pbtnbtm td a {background:#fff;padding:0;border:0;}
  1760. .hld__excel-body #m_posts .comment_c .comment_c_1 {border-top-color:#bbbbbb;}
  1761. .hld__excel-body #m_posts .comment_c .comment_c_2 {border-color:#bbbbbb;}
  1762. .hld__excel-body #m_posts {border:0;box-shadow:none;padding-bottom:0;margin:0;counter-reset:num;}
  1763. .hld__excel-body #m_posts td {background:#fff;border-top:1px solid #bbbbbb;border-right:1px solid #bbbbbb;border-bottom:1px solid #bbbbbb;}
  1764. .hld__excel-body #m_posts .c0 {width:32px;color:#777;font-size:16px;background:#e8e8e8;text-align:center;}
  1765. .hld__excel-body #m_posts .c0:before {content:counter(num);counter-increment:num;}
  1766. .hld__excel-body #m_posts .vertmod {background:#fff !important;color:#ccc;}
  1767. .hld__excel-body #m_posts a[name="uid"]:before {content:"UID:"}
  1768. .hld__excel-body #m_posts .white,.hld__excel-body #m_posts .block_txt_c2,.hld__excel-body #m_posts .block_txt_c0 {background:#fff !important;color:#777777;}
  1769. .hld__excel-body #m_posts .quote {background:#fff;border-color:#bbbbbb;}
  1770. .hld__excel-body #m_posts .postrow .postinfob .iconfont,.hld__excel-body #m_posts .ogoodbtn a:hover .iconfont {fill: #10273f;}
  1771. .hld__excel-body #m_posts .postInfo svg {fill:#10273f !important;}
  1772. .hld__excel-body #m_posts .recommendvalue {color:#10273f !important;}
  1773. .hld__excel-body #m_posts button {background:#eee;}
  1774. .hld__excel-body #m_posts button:active {outline-color:#bbbbbb;}
  1775. .hld__excel-body #m_posts .postbox {border:none !important;}
  1776. .hld__excel-body .posterInfoLine {background: #FFF !important;border-bottom-color: #FFF !important;}
  1777. .hld__excel-body.hld__reply-fixed #postbbtm {position:fixed;right:30px;top:75px;z-index:999;border-radius: 10px;overflow: hidden;}
  1778. .hld__excel-body .row2 .comment_c .comment_c_1_1 {border-top-color: #FFF;}
  1779. .hld__excel-body #m_posts .comment_c .comment_c_1 {border-color: #FFF;border-top-color: #BBB;}
  1780. /* Office风格 */
  1781. .hld__excel-body.hld__excel-theme-office .hld__excel-header {height:221px;}
  1782. .hld__excel-body.hld__excel-theme-office .hld__excel-h1 {height:59px;background:#227447;display:flex;justify-content: center;}
  1783. .hld__excel-body.hld__excel-theme-office .hld__excel-title {display: block;color:#FFF;font-size: 12px;font-weight: 400;font-family: sans-serif;line-height:30px;}
  1784. .hld__excel-body.hld__excel-theme-office .hld__excel-h2 {height:95px;background:#f1f1f1;border-bottom:1px solid #d5d5d5;}
  1785. .hld__excel-body.hld__excel-theme-office .hld__excel-h3 {height:48px;background:#e6e6e6;box-shadow:none;}
  1786. .hld__excel-body.hld__excel-theme-office .hld__excel-fx {left:250px;right: 5px;border-color:#c6c6c6;border-radius:0;height:28px;}
  1787. .hld__excel-body.hld__excel-theme-office .hld__excel-h4 {height:20px;}
  1788. .hld__excel-body.hld__excel-theme-office .hld__excel-f1 {height:29px;}
  1789. .hld__excel-body.hld__excel-theme-office .hld__excel-f2 {height:21px;}
  1790. .hld__excel-body.hld__excel-theme-office .hld__excel-f1 {border-top:1px solid #999999;border-bottom:1px solid #bfbfbf;}
  1791. .hld__excel-body.hld__excel-theme-office .hld__excel-img-f1-l1, .hld__excel-body.hld__excel-theme-office .hld__excel-img-f1-r1 {top:-1px;}
  1792. .hld__excel-body.hld__excel-theme-office #mmc {margin-top:221px;}
  1793. .hld__excel-body.hld__excel-theme-office #m_nav {top:160px;}
  1794. .hld__excel-body.hld__excel-theme-office #m_posts .c0,
  1795. .hld__excel-body.hld__excel-theme-office .topicrow .c1 {width:32px;}
  1796. .hld__excel-body.hld__excel-theme-office #pagebbtm:before,
  1797. .hld__excel-body.hld__excel-theme-office .topicrow .c1 a {width:28px;}
  1798. .hld__excel-body.hld__excel-theme-office .hld__excel-setting {top: 36px;background:none;text-align: center;}
  1799. .hld__excel-body.hld__excel-theme-office .hld__excel-setting a {color:#FFFFFF;}
  1800. .hld__excel-body.hld__excel-theme-office .hld__excel-setting img {display:none;}
  1801. .hld__excel-body.hld__excel-theme-office.hld__reply-fixed #postbbtm {top: 162px;}
  1802. .hld__excel-body.hld__excel-theme-office #m_pbtnbtm td a,
  1803. .hld__excel-body.hld__excel-theme-office #m_pbtnbtm .stdbtn {background: none;}
  1804. .hld__excel-body.hld__excel-theme-office #mainmenu {top:35px;right:45px;}
  1805. .hld__excel-body.hld__excel-theme-office #mainmenu .mmdefault.cell input {border-radius:0;}
  1806. .hld__excel-body.hld__excel-theme-office #mainmenu .stdbtn a, .hld__excel-body.hld__excel-theme-office #mainmenu .hld__half-clone {color:#FFF !important;}
  1807. .hld__excel-body.hld__excel-theme-office .single_ttip2 {top:59px !important;}
  1808. /* 腾讯文档风格 */
  1809. .hld__excel-body.hld__excel-theme-tencent {font-family: -apple-system, Helvetica Neue, Helvetica, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif !important;}
  1810. .hld__excel-body.hld__excel-theme-tencent .hld__excel-header {height:125px;background:#FFF;}
  1811. .hld__excel-body.hld__excel-theme-tencent .hld__excel-titlebar-title {height: 36px;line-height: 36px;font-size: 18px;font-weight: 500;color: #000;opacity: 0.88;margin: 0 9px;max-width: 30%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
  1812. .hld__excel-body.hld__excel-theme-tencent #mmc {margin-top: 145px;}
  1813. .hld__excel-body.hld__excel-theme-tencent #m_nav {top: 94px;left: 65px;}
  1814. .hld__excel-body.hld__excel-theme-tencent .hld__excel-sub {width: 51px;}
  1815. .hld__excel-body.hld__excel-theme-tencent .hld__excel-titlebar {height:56px;display: flex;align-items: center;flex-shrink: 0;padding: 0 4px;border-bottom:1px solid #ebebeb;}
  1816. .hld__excel-body.hld__excel-theme-tencent .hld__excel-toolbar {height:44px;display: flex;align-items: center;flex-shrink: 0;padding: 0 12px;border-bottom:1px solid #ebebeb;line-height: 24px;font-size: 12px;color:rgba(0, 0, 0, 0.88);font-weight:400;}
  1817. .hld__excel-body.hld__excel-theme-tencent .hld__excel-toolbar > div {flex-shrink: 0;}
  1818. .hld__excel-body.hld__excel-theme-tencent .hld__excel-titlebar-pick {margin-left: 12px;margin-top: -2px;}
  1819. .hld__excel-body.hld__excel-theme-tencent .hld__excel-titlebar-pick .hld__excel-titlebar-content {width:17px;height:17px}
  1820. .hld__excel-body.hld__excel-theme-tencent .hld__excel-titlebar-pick .hld__excel-titlebar-indication {height: 3px;width: 14px;margin-left: 2px;margin-top: -2px;}
  1821. .hld__excel-body.hld__excel-theme-tencent .hld__excel-formulabar {height:25px;}
  1822. .hld__excel-body.hld__excel-theme-tencent .hld__excel-icon24 {width:24px;height:24px;background-size: 100% 100%;}
  1823. .hld__excel-body.hld__excel-theme-tencent .hld__excel-icon20 {width:20px;height:20px;background-size: 100% 100%;}
  1824. .hld__excel-body.hld__excel-theme-tencent .hld__excel-icon16 {width:16px;height:16px;background-size: 100% 100%;}
  1825. .hld__excel-body.hld__excel-theme-tencent .hld__excel-icon12 {width:12px;height:12px;background-size: 100% 100%;}
  1826. .hld__excel-body.hld__excel-theme-tencent .hld__excel-h4 > div {background-color:#f9fafb;border-bottom: 1px solid #ebebeb;border-top: 1px solid #ebebeb;border-color:#ebebeb;}
  1827. .hld__excel-body.hld__excel-theme-tencent #m_posts .c0, .hld__excel-body.hld__excel-theme-tencent .topicrow .c1, .hld__excel-body.hld__excel-theme-tencent #pagebbtm:before {width:50px;background-color:#f9fafb !important;}
  1828. .hld__excel-body.hld__excel-theme-tencent #topicrows td, .hld__excel-body.hld__excel-theme-tencent #m_posts td {border-color:#ebebeb;}
  1829. .hld__excel-body.hld__excel-theme-tencent .hld__excel-footer {height:32px;background:#FFF;display:flex;align-items: center;border-top: 1px solid #e0e0e0;padding: 0 10px;}
  1830. .hld__excel-body.hld__excel-theme-tencent .hld__excel-sheet-tab {margin-left: 8px;width:104px;border: 1px solid #e0e0e0;border-top: 1px solid #fff;text-align:center;height: 30px;}
  1831. .hld__excel-body.hld__excel-theme-tencent .hld__excel-sheet-tab .hld__excel-sheet-name {font-size: 14px;color: rgba(0,0,0,.88);font-weight: 400;height: 26px;line-height: 26px;border-bottom:2px solid #1e6fff;display:flex;justify-content: center;align-items: center;}
  1832. .hld__excel-body.hld__excel-theme-tencent .hld__excel-footer-item {color:#464d5a;font-size:14px;margin:0 4px;height: 32px;line-height: 32px;}
  1833. .hld__excel-body.hld__excel-theme-tencent #mainmenu {top: 18px;right: 20px;}
  1834. .hld__excel-body.hld__excel-theme-tencent #postbbtm {top: 60px;right: 5px;}
  1835. .hld__excel-body.hld__excel-theme-tencent #m_pbtnbtm .stdbtn, .hld__excel-body.hld__excel-theme-tencent #m_pbtnbtm .stdbtn a {background: none;font-weight:400;}
  1836. .hld__excel-body.hld__excel-theme-tencent #m_pbtnbtm .uitxt1 span {font-size: 1em !important;color: #10273f;}
  1837. .hld__excel-body.hld__excel-theme-tencent #mainmenu .mmdefault.cell input {background: #FFF;}
  1838. `
  1839. }
  1840. /**
  1841. * 折叠引用模块
  1842. * @name FoldQuote
  1843. * @description 此模块提供了可以选择配置自动折叠过长引用
  1844. * 提供一个高级配置可以设置折叠的阈值
  1845. */
  1846. const FoldQuote = {
  1847. name: 'FoldQuote',
  1848. title: '折叠引用',
  1849. settings: [{
  1850. type: 'normal',
  1851. key: 'foldQuote',
  1852. default: true,
  1853. title: '折叠过长引用与附件',
  1854. menu: 'left'
  1855. },{
  1856. type: 'advanced',
  1857. key: 'foldQuoteHeight',
  1858. default: 300,
  1859. title: '自动折叠引用高度',
  1860. desc: '自动折叠引用的高度阈值,单位为像素(px)',
  1861. menu: 'right'
  1862. }],
  1863. renderFormsFunc($el) {
  1864. if (script.setting.normal.foldQuote) {
  1865. // 自动折叠过长引用
  1866. $el.find('.postcontent .quote').each(function() {
  1867. const $quote = $(this)
  1868. if ($quote.height() > (script.setting.advanced.foldQuoteHeight || 300)) {
  1869. const originalHeight = $quote.height()
  1870. $quote.addClass('hld__quote-fold')
  1871. const foldHeight = $quote.height()
  1872. const $openBtn = $(`<div class="hld__quote-box"><button>查看全部 (剩余${100-parseInt(foldHeight/originalHeight*100)}%)</button></div>`)
  1873. $openBtn.on('click', 'button', function(){
  1874. $(this).parent().remove()
  1875. $quote.removeClass('hld__quote-fold')
  1876. })
  1877. $quote.append($openBtn)
  1878. }
  1879. })
  1880. // 折叠附件
  1881. if ($el.find('h4.silver.subtitle').length > 0) {
  1882. $el.find('h4.silver.subtitle').each(function (){
  1883. if ($(this).html() === '附件' && $(this).next().attr('id').includes('postattach')) {
  1884. const $attach = $(this).next()
  1885. $attach.hide()
  1886. const $openBtn = $(`<button>显示附件</button>`)
  1887. $openBtn.on('click', function(){
  1888. $(this).remove()
  1889. $attach.show()
  1890. })
  1891. $(this).next().after($openBtn)
  1892. }
  1893. })
  1894. }
  1895. }
  1896. },
  1897. style: `
  1898. .hld__quote-fold{height:150px;overflow:hidden;position: relative;}
  1899. .hld__quote-box{padding:10px;position: absolute;left:0;right:0;bottom:0;background:#f2eddf;}
  1900. .hld__excel-body .hld__quote-box{background:#FFF;}
  1901. `
  1902. }
  1903. /**
  1904. * 新页面打开模块
  1905. * @name LinkTargetBlank
  1906. * @description 此模块提供了可以选择配置在新页面打开链接
  1907. */
  1908. const LinkTargetBlank = {
  1909. name: 'LinkTargetBlank',
  1910. title: '新页面打开',
  1911. setting: {
  1912. type: 'normal',
  1913. key: 'linkTargetBlank',
  1914. default: false,
  1915. title: '论坛列表新窗口打开',
  1916. menu: 'right'
  1917. },
  1918. renderThreadsFunc($el) {
  1919. if (script.setting.normal.linkTargetBlank) {
  1920. let $link = $el.find('.topic')
  1921. $link.data('href', $link.attr('href')).attr('href', 'javascript:void(0)')
  1922. $link.click(() => {
  1923. window.open($link.data('href'))
  1924. return false
  1925. })
  1926. }
  1927. }
  1928. }
  1929. /**
  1930. * 链接直接跳转
  1931. * @name DirectLinkJump
  1932. * @description 此模块提供了超链接等直接跳转无须弹窗确认
  1933. */
  1934. const DirectLinkJump = {
  1935. name: 'DirectLinkJump',
  1936. title: '链接直接跳转',
  1937. setting: {
  1938. type: 'normal',
  1939. key: 'directLinkJump',
  1940. default: true,
  1941. title: '链接直接跳转',
  1942. menu: 'right'
  1943. },
  1944. renderFormsFunc($el) {
  1945. if (script.setting.normal.directLinkJump) {
  1946. $el.find('a[onclick]').each(function(){
  1947. if ($(this).attr('onclick').includes('showUrlAlert')) {
  1948. $(this).removeAttr('onclick onmouseover onmouseout')
  1949. }
  1950. })
  1951. }
  1952. }
  1953. }
  1954. /**
  1955. * 图片增强模块
  1956. * @name ImgEnhance
  1957. * @description 此模块提供了图片增强功能,使用一个独立的图层打开图片
  1958. * 可以快速切换,缩放,旋转等
  1959. */
  1960. const ImgEnhance = {
  1961. name: 'ImgEnhance',
  1962. title: '图片增强',
  1963. settings: [{
  1964. type: 'normal',
  1965. key: 'imgEnhance',
  1966. default: true,
  1967. title: '贴内图片功能增强',
  1968. menu: 'right'
  1969. }, {
  1970. shortCutCode: 37, // LEFT
  1971. key: 'imgEnhancePrev',
  1972. title: '楼内上一张图'
  1973. }, {
  1974. shortCutCode: 39, // RIGHT
  1975. key: 'imgEnhanceNext',
  1976. title: '楼内上一张图'
  1977. }],
  1978. renderFormsFunc($el) {
  1979. $el.find('img').each(function () {
  1980. const classs = $(this).attr('class')
  1981. if (!classs || (classs && !classs.includes('smile'))) {
  1982. $(this).attr('hld__imglist', 'ready').removeAttr('onload').removeAttr('onclick')
  1983. }
  1984. })
  1985. //图片增强
  1986. if (script.setting.normal.imgEnhance) {
  1987. const _this = this
  1988. $('#mc').on('click', '.postcontent img[hld__imglist=ready]', function () {
  1989. _this.resizeImg($(this))
  1990. return false
  1991. })
  1992. }
  1993. },
  1994. resizeImg(el) {
  1995. if ($('#hld__img_full').length > 0) return
  1996. let urlList = []
  1997. let currentIndex = el.parent().find('[hld__imglist=ready]').index(el)
  1998. el.parent().find('[hld__imglist=ready]').each(function () {
  1999. if ($(this).attr('src') != 'about:blank') {
  2000. urlList.push($(this).data('srcorg') || $(this).data('srclazy') || $(this).attr('src'))
  2001. }
  2002. })
  2003. let $imgBox = $('<div id="hld__img_full" title="点击背景关闭"><div id="loader"></div></div>')
  2004. let $imgContainer = $('<div class="hld__img_container hld__zoom-target"></div>')
  2005. let $img = $('<img title="鼠标滚轮放大/缩小\n左键拖动移动" class="hld__img hld__zoom-target">')
  2006.  
  2007. const renderImg = (index) => {
  2008. let timer = null
  2009. $('#loader').show()
  2010. $imgContainer.css({
  2011. 'top': $(window).height() * 0.03 + 'px',
  2012. 'left': (($(window).width() - ($(window).height()) * 0.85) / 2) + 'px',
  2013. 'width': $(window).height() * 0.85 + 'px',
  2014. 'height': $(window).height() * 0.85 + 'px'
  2015. })
  2016. $img.css({ 'width': '', 'height': '' }).attr('src', urlList[index]).hide()
  2017. timer = setInterval(() => {
  2018. const w = $img.width()
  2019. const h = $img.height()
  2020. if (w > 0) {
  2021. w > h ? $img.css({ 'width': '100%', 'height': 'auto' }) : $img.css({ 'height': '100%', 'width': 'auto' })
  2022. $img.show()
  2023. $('#loader').hide()
  2024. clearInterval(timer)
  2025. }
  2026. }, 1)
  2027. }
  2028. //当前图片
  2029. renderImg(currentIndex)
  2030. $img.mousedown(function (e) {
  2031. let endx = 0;
  2032. let endy = 0;
  2033. let left = parseInt($imgContainer.css("left"))
  2034. let top = parseInt($imgContainer.css("top"))
  2035. let downx = e.pageX
  2036. let downy = e.pageY
  2037. e.preventDefault()
  2038. $(document).on("mousemove", function (es) {
  2039. endx = es.pageX - downx + left
  2040. endy = es.pageY - downy + top
  2041. $imgContainer.css("left", endx + "px").css("top", endy + "px")
  2042. return false
  2043. });
  2044. })
  2045. $img.mouseup(function () { $(document).unbind("mousemove") })
  2046. $imgContainer.append($img)
  2047. $imgBox.append($imgContainer)
  2048. $imgBox.click(function (e) { !$(e.target).hasClass('hld__img') && $(this).remove() })
  2049. $imgBox.append(`
  2050. <div class="hld__if_control">
  2051. <div class="change prev-img" title="本楼内上一张"><div></div></div>
  2052. <div class="change rotate-right" title="逆时针旋转90°"><div></div></div>
  2053. <div class="change rotate-left" title="顺时针旋转90°"><div></div></div>
  2054. <div class="change next-img" title="本楼内下一张"><div></div></div>
  2055. </div>
  2056. `)
  2057. /**
  2058. * Bind:Click
  2059. * 切换图片
  2060. */
  2061. $imgBox.on('click', '.change', function () {
  2062. if ($(this).hasClass('prev-img') && currentIndex - 1 >= 0)
  2063. renderImg(--currentIndex)
  2064.  
  2065. if ($(this).hasClass('next-img') && currentIndex + 1 < urlList.length)
  2066. renderImg(++currentIndex)
  2067.  
  2068. if ($(this).hasClass('rotate-right') || $(this).hasClass('rotate-left')) {
  2069. let deg = ($img.data('rotate-deg') || 0) - ($(this).hasClass('rotate-right') ? 90 : -90)
  2070. if (deg >= 360 || deg <= -360) deg = 0
  2071. $img.css('transform', `rotate(${deg}deg)`)
  2072. $img.data('rotate-deg', deg)
  2073. } else {
  2074. $img.css('transform', '')
  2075. $img.data('rotate-deg', 0)
  2076. }
  2077. window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty()
  2078. return false;
  2079. })
  2080. /**
  2081. * Bind:MouseWheel
  2082. * 大图鼠标滚动缩放
  2083. */
  2084. $imgBox.on("mousewheel DOMMouseScroll", function (e) {
  2085. const delta = (e.originalEvent.wheelDelta && (e.originalEvent.wheelDelta > 0 ? 1 : -1)) ||
  2086. (e.originalEvent.detail && (e.originalEvent.detail > 0 ? -1 : 1));
  2087.  
  2088. if ($imgContainer.width() > 50 || delta > 0) {
  2089. const offsetY = $imgContainer.height() * 0.2
  2090. const offsetX = $imgContainer.width() * 0.2
  2091. let offsetTop = offsetY / 2
  2092. let offsetLeft = offsetX / 2
  2093.  
  2094. if ($(e.target).hasClass('hld__zoom-target')) {
  2095. const targetOffsetX = Math.round(e.clientX - $imgContainer.position().left)
  2096. const targetOffsetY = Math.round(e.clientY - $imgContainer.position().top)
  2097. offsetLeft = (targetOffsetX / ($imgContainer.height() / 2)) * offsetLeft
  2098. offsetTop = (targetOffsetY / ($imgContainer.height() / 2)) * offsetTop
  2099. }
  2100.  
  2101. if (delta > 0) {
  2102. $imgContainer.css({
  2103. 'width': ($imgContainer.height() + offsetY) + 'px',
  2104. 'height': ($imgContainer.height() + offsetY) + 'px',
  2105. 'top': ($imgContainer.position().top - offsetTop) + 'px',
  2106. 'left': ($imgContainer.position().left - offsetLeft) + 'px'
  2107. })
  2108. }
  2109. if (delta < 0) {
  2110. $imgContainer.css({
  2111. 'width': ($imgContainer.height() - offsetY) + 'px',
  2112. 'height': ($imgContainer.height() - offsetY) + 'px',
  2113. 'top': ($imgContainer.position().top + offsetTop) + 'px',
  2114. 'left': ($imgContainer.position().left + offsetLeft) + 'px'
  2115. })
  2116. }
  2117. }
  2118. e.stopPropagation()
  2119. return false
  2120. })
  2121. /**
  2122. * Bind:Keyup
  2123. * Esc关闭大图
  2124. */
  2125. $('body').keyup(event => (event.keyCode == 27 && $('#hld__img_full').length > 0) && $('#hld__img_full').remove())
  2126. $('body').append($imgBox)
  2127. },
  2128. shortcutFunc: {
  2129. imgEnhancePrev() {
  2130. if ($('#hld__img_full').length > 0) {
  2131. $('#hld__img_full .prev-img').click()
  2132. }
  2133. },
  2134. imgEnhanceNext() {
  2135. if ($('#hld__img_full').length > 0) {
  2136. $('#hld__img_full .next-img').click()
  2137. }
  2138. }
  2139. },
  2140. style: `
  2141. .hld__img_container {position:absolute;display:flex;justify-content:center;align-items:center;}
  2142. .hld__if_control {position:absolute;display:flex;left:50%;bottom:15px;width:160px;margin-left:-80px;height:40px;background:rgba(0,0,0,0.6);z-index:9999999;}
  2143. .postcontent img {margin:0 5px 5px 0 !important;box-shadow:none !important;outline:none !important;max-height: none !important;}
  2144. #hld__img_full {position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:99999;}
  2145. #hld__img_full img {cursor:move;transition:transform .2s ease;}
  2146. #hld__img_full .hld__imgcenter {top:50%;left:50%;transform:translate(-50%,-50%);}
  2147. #hld__img_full .change {width:40px;height:40px;cursor:pointer;}
  2148. #hld__img_full .rotate-right,#hld__img_full .rotate-left {background:url(${IMG_ICON_REFRESH}) center no-repeat;background-size:25px;}
  2149. #hld__img_full .rotate-right {transform:rotateY(180deg);}
  2150. #hld__img_full .rotate-left:hover {transform:scale(1.2);}
  2151. #hld__img_full .rotate-right:hover {transform:scale(1.2) rotateY(180deg);}
  2152. #hld__img_full .next-img:hover {transform:scale(1.2) rotate(180deg);}
  2153. #hld__img_full .prev-img,#hld__img_full .next-img {background:url(${IMG_ICON_LEFT}) center no-repeat;}
  2154. #hld__img_full .next-img {transform:rotate(180deg);}
  2155. #hld__img_full .prev-img:hover {transform:scale(1.2);}
  2156. #hld__img_full .next-img:hover {transform:scale(1.2) rotate(180deg);}
  2157. `
  2158. }
  2159. /**
  2160. * 标记楼主模块
  2161. * @name AuthorMark
  2162. * @requires https://cdn.staticfile.org/spectrum/1.8.0/spectrum.js
  2163. * @description 此模块提供了自动标记楼主,使其更醒目
  2164. * 提供了高级设置可选标记楼主的颜色
  2165. * 以及为其他模块提供一个spectrum的配置文件
  2166. */
  2167. const AuthorMark = {
  2168. name: 'AuthorMark',
  2169. title: '标记楼主',
  2170. settings: [{
  2171. type: 'normal',
  2172. key: 'authorMark',
  2173. default: true,
  2174. title: '高亮楼主',
  2175. menu: 'right'
  2176. }, {
  2177. type: 'advanced',
  2178. key: 'authorMarkColor',
  2179. default: '#F00',
  2180. title: '标记楼主颜色',
  2181. desc: '标记楼主中的[楼主]的背景颜色,单位为16进制颜色代码',
  2182. menu: 'left'
  2183. }],
  2184. // spectrum配置对象
  2185. colorPickerConfig: {
  2186. type: 'color',
  2187. preferredFormat: 'hex',
  2188. showPaletteOnly: 'true',
  2189. togglePaletteOnly: 'true',
  2190. hideAfterPaletteSelect: 'true',
  2191. showAlpha: 'false',
  2192. togglePaletteMoreText: '更多选项',
  2193. togglePaletteLessText: '隐藏',
  2194. palette: [
  2195. ['#000000','#444444','#5b5b5b','#999999','#bcbcbc','#eeeeee','#f3f6f4','#ffffff'],
  2196. ['#f44336','#744700','#ce7e00','#8fce00','#2986cc','#16537e','#6a329f','#c90076'],
  2197. ['#f4cccc','#fce5cd','#fff2cc','#d9ead3','#d0e0e3','#cfe2f3','#d9d2e9','#ead1dc'],
  2198. ['#ea9999','#f9cb9c','#ffe599','#b6d7a8','#a2c4c9','#9fc5e8','#b4a7d6','#d5a6bd'],
  2199. ['#e06666','#f6b26b','#ffd966','#93c47d','#76a5af','#6fa8dc','#8e7cc3','#c27ba0'],
  2200. ['#cc0000','#e69138','#f1c232','#6aa84f','#45818e','#3d85c6','#674ea7','#a64d79'],
  2201. ['#990000','#b45f06','#bf9000','#38761d','#134f5c','#0b5394','#351c75','#741b47'],
  2202. ['#660000','#783f04','#7f6000','#274e13','#0c343d','#073763','#20124d','#4c1130']
  2203. ]
  2204. },
  2205. postAuthor: [],
  2206. initFunc() {
  2207. const localPostAuthor = script.getValue('hld__NGA_post_author')
  2208. localPostAuthor && (this.postAuthor = localPostAuthor.split(','))
  2209. // 初始化颜色选择器
  2210. this.initSpectrum('#hld__setting_cover #hld__adv_authorMarkColor')
  2211. },
  2212. renderFormsFunc($el) {
  2213. const _this = this
  2214. if (script.setting.normal.authorMark) {
  2215. const author = $('#postauthor0').text().replace('楼主', '')
  2216. const tid = this.getQueryString('tid')
  2217. const authorStr = `${tid}:${author}`
  2218. if (author && !this.postAuthor.includes(authorStr) && ['authorid=', 'pid='].every(k => !window.location.href.includes(k))) {
  2219. this.postAuthor.unshift(authorStr) > 10 && this.postAuthor.pop()
  2220. script.setValue('hld__NGA_post_author', this.postAuthor.join(','))
  2221. }
  2222. $el.find('a.b').each(function () {
  2223. const name = $(this).attr('hld-mark-before-name') || $(this).text().replace('[', '').replace(']', '')
  2224. if (name && _this.postAuthor.includes(`${tid}:${name}`)) {
  2225. $(this).append('<span class="hld__post-author">楼主</span>')
  2226. }
  2227. })
  2228. }
  2229. },
  2230. /**
  2231. * 获取URL参数
  2232. * @method getQueryString
  2233. * @param {String} name key
  2234. * @param {String} url 要解析的URL
  2235. * @return {String|null} value
  2236. */
  2237. getQueryString(name, url='') {
  2238. url ||= decodeURIComponent(window.location.href.replace(/&amp;/g, "&"))
  2239. let reg = new RegExp("(?:\\?|&)" + name + "=([^&]*)(&|$)")
  2240. let r = url.substring(1).match(reg)
  2241. if (r != null) return encodeURIComponent(r[1])
  2242. return null
  2243. },
  2244. /**
  2245. * 初始化颜色选择器
  2246. * @method initSpectrum
  2247. * @param {str} selector 元素选择器
  2248. */
  2249. initSpectrum(selector) {
  2250. if (selector instanceof jQuery) {
  2251. selector.spectrum(this.colorPickerConfig)
  2252. } else {
  2253. $(selector).spectrum(this.colorPickerConfig)
  2254. }
  2255. },
  2256. asyncStyle() {
  2257. return `
  2258. .hld__post-author {background:${script.setting.advanced.authorMarkColor || '#F00'};color: #FFF;display: inline-block;padding:0 5px;margin-left: 5px;border-radius: 5px;font-weight:bold;line-height: 1.4em;padding-top: 0.1em;padding-bottom: 0;}
  2259. `
  2260. },
  2261. style: `
  2262. .cp-color-picker{z-index:99997}
  2263. .sp-container{position:absolute;top:0;left:0;display:inline-block;z-index:9999994;overflow:hidden}
  2264. .sp-original-input-container{position:relative;display:inline-flex}
  2265. .sp-original-input-container input{margin:0!important}
  2266. .sp-original-input-container .sp-add-on{width:40px;border-top-right-radius:0!important;border-bottom-right-radius:0!important}
  2267. input.spectrum.with-add-on{border-top-left-radius:0;border-bottom-left-radius:0;border-left:0}
  2268. .sp-original-input-container .sp-add-on .sp-colorize{height:100%;width:100%;border-radius:inherit}
  2269. .sp-colorize-container{background-image:url(${IMG_ICON_ALPHA})}
  2270. .sp-container.sp-flat{position:relative}
  2271. .sp-container,.sp-container *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}
  2272. .sp-top{position:relative;width:100%;display:inline-block}
  2273. .sp-top-inner{position:absolute;top:0;left:0;bottom:0;right:0}
  2274. .sp-color{position:absolute;top:0;left:0;bottom:0;right:20px!important}
  2275. .sp-hue{position:absolute;top:0;right:0;bottom:0;width:12px;height:100%;left:initial!important}
  2276. .sp-clear-enabled .sp-hue{top:15%;height:85%}
  2277. .sp-fill{padding-top:80%}
  2278. .sp-sat,.sp-val{position:absolute;top:0;left:0;right:0;bottom:0}
  2279. .sp-alpha-enabled .sp-top{margin-bottom:28px!important}
  2280. .sp-alpha-enabled .sp-alpha{display:block}
  2281. .sp-alpha-handle{position:absolute;top:-3px;cursor:pointer;height:16px;border-radius:50%;width:16px;margin-right:5px;left:-2px;right:0;background:#f9f9f9;box-shadow:0 0 2px 0 #3a3a3a}
  2282. .sp-alpha{display:none;position:absolute;bottom:-18px;right:0;left:0;height:10px}
  2283. .sp-alpha-inner{border-radius:4px}
  2284. .sp-clear{display:none}
  2285. .sp-clear.sp-clear-display{background-position:center}
  2286. .sp-clear-enabled .sp-clear{display:block;position:absolute;top:3px;right:0;bottom:0;cursor:pointer;left:initial;height:12px;width:12px}
  2287. .sp-alpha,.sp-alpha-handle,.sp-clear,.sp-container,.sp-container button,.sp-container.sp-dragging .sp-input,.sp-dragger,.sp-preview,.sp-replacer,.sp-slider{-webkit-user-select:none;-moz-user-select:-moz-none;-o-user-select:none;user-select:none}
  2288. .sp-container.sp-input-disabled .sp-input-container{display:none}
  2289. .sp-container.sp-buttons-disabled .sp-button-container{display:none}
  2290. .sp-container.sp-palette-buttons-disabled .sp-palette-button-container{display:none}
  2291. .sp-palette-only .sp-picker-container{display:none}
  2292. .sp-palette-disabled .sp-palette-container{display:none}
  2293. .sp-initial-disabled .sp-initial{display:none}
  2294. .sp-sat{background-image:-webkit-gradient(linear,0 0,100% 0,from(#fff),to(rgba(204,154,129,0)));background-image:-webkit-linear-gradient(left,#fff,rgba(204,154,129,0));background-image:-moz-linear-gradient(left,#fff,rgba(204,154,129,0));background-image:-o-linear-gradient(left,#fff,rgba(204,154,129,0));background-image:-ms-linear-gradient(left,#fff,rgba(204,154,129,0));background-image:linear-gradient(to right,#fff,rgba(204,154,129,0))}
  2295. .sp-val{border-radius:4px;background-image:-webkit-gradient(linear,0 100%,0 0,from(#000),to(rgba(204,154,129,0)));background-image:-webkit-linear-gradient(bottom,#000,rgba(204,154,129,0));background-image:-moz-linear-gradient(bottom,#000,rgba(204,154,129,0));background-image:-o-linear-gradient(bottom,#000,rgba(204,154,129,0));background-image:-ms-linear-gradient(bottom,#000,rgba(204,154,129,0));background-image:linear-gradient(to top,#000,rgba(204,154,129,0))}
  2296. .sp-hue{background:-moz-linear-gradient(top,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);background:-ms-linear-gradient(top,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);background:-o-linear-gradient(top,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);background:-webkit-gradient(linear,left top,left bottom,from(red),color-stop(.17,#ff0),color-stop(.33,#0f0),color-stop(.5,#0ff),color-stop(.67,#00f),color-stop(.83,#f0f),to(red));background:-webkit-linear-gradient(top,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);background:linear-gradient(to bottom,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}
  2297. .sp-1{height:17%}
  2298. .sp-2{height:16%}
  2299. .sp-3{height:17%}
  2300. .sp-4{height:17%}
  2301. .sp-5{height:16%}
  2302. .sp-6{height:17%}
  2303. .sp-hidden{display:none!important}
  2304. .sp-cf:after,.sp-cf:before{content:"";display:table}
  2305. .sp-cf:after{clear:both}
  2306. @media (max-device-width:480px){.sp-color{right:40%}
  2307. .sp-hue{left:63%}
  2308. .sp-fill{padding-top:60%}
  2309. }
  2310. .sp-dragger{border-radius:5px;height:10px;width:10px;border:1px solid #fff;cursor:pointer;position:absolute;top:0;left:0;margin-left:3px;margin-top:3px;box-shadow:0 0 2px 1px rgba(0,0,0,.2)}
  2311. .sp-slider{position:absolute;top:0;cursor:pointer;height:16px;border-radius:50%;width:16px;left:-2px;background:#f9f9f9;box-shadow:0 0 2px 0 #3a3a3a;margin-top:8px}
  2312. .sp-container{display:inline-flex;border-radius:0;background-color:#fff;padding:0;border-radius:4px;color:#000;box-shadow:0 0 0 1px rgba(99,114,130,.16),0 8px 16px rgba(27,39,51,.08)}
  2313. .sp-clear,.sp-color,.sp-container,.sp-container button,.sp-container input,.sp-hue{font-size:12px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}
  2314. .sp-top{margin-bottom:10px}
  2315. .sp-clear,.sp-color,.sp-hue,.sp-sat,.sp-val{border-radius:3px}
  2316. .sp-input-container{margin-top:-5px}
  2317. .sp-button-container.sp-cf,.sp-initial.sp-thumb.sp-cf,.sp-input-container.sp-cf{height:25px}
  2318. .sp-picker-container .sp-cf{margin-bottom:10px}
  2319. .sp-palette-row-initial>span:first-child{cursor:pointer}
  2320. .sp-initial-disabled .sp-input-container{width:100%}
  2321. .sp-input{padding:0 5px!important;margin:0;width:100%;box-shadow:none!important;height:100%!important;background:0 0;color:#3a3a3a;border-radius:2px!important;border:1px solid #e0e0e0!important;text-align:center;font-family:monospace;font-size:inherit!important}
  2322. .sp-input.sp-validation-error{border:1px solid red;background:#fdd}
  2323. .sp-palette-container,.sp-picker-container{position:relative;padding:10px}
  2324. .sp-picker-container{width:200px;padding-bottom:0}
  2325. .sp-palette-container{border-right:solid 1px #ccc}
  2326. .sp-palette-only .sp-palette-container{border:0}
  2327. .sp-palette .sp-thumb-el{display:block;position:relative;float:left;width:24px;height:15px;margin:3px;cursor:pointer;border:solid 2px transparent}
  2328. .sp-palette .sp-thumb-el.sp-thumb-active,.sp-palette .sp-thumb-el:hover{border-color:orange}
  2329. .sp-thumb-el{position:relative}
  2330. .sp-initial{float:left}
  2331. .sp-initial span{width:30px;height:25px;border:none;display:block;float:left;margin:0}
  2332. .sp-initial .spe-thumb-el.sp-thumb-active{border-radius:0 5px 5px 0}
  2333. .sp-initial .spe-thumb-el{border-radius:5px 0 0 5px}
  2334. .sp-initial .sp-clear-display{background-position:center}
  2335. .sp-button-container{float:right;display:none;}
  2336. .sp-palette-button-container{margin-top:10px}
  2337. .sp-replacer{position:relative;overflow:hidden;cursor:pointer;display:inline-block;border-radius:3px;border:1px solid #aaa;color:#666;transition:border-color .3s;vertical-align:middle;width:40px;height:20px;margin: 1.5px 3px;}
  2338. .sp-replacer.sp-active,.sp-replacer:hover{border:1px solid #666;color:#000}
  2339. .sp-replacer.sp-disabled{cursor:default;border-color:silver;color:silver}
  2340. .sp-dd{position:absolute;font-size:10px;right:0;top:0;bottom:0;padding:0 1px;line-height:22px;background-color:#fff;border-left: 1px solid #aaa;}
  2341. .sp-preview{position:relative;width:100%;height:100%;float:left;z-index:0}
  2342. .sp-preview-inner{transition:background-color .2s}
  2343. .sp-preview-inner.sp-clear-display{display:none}
  2344. .sp-palette .sp-thumb-el{width:16px;height:16px;margin:3px;border:none;border-radius:3px}
  2345. .sp-container button{border-radius:3px;border:none;background:0 0;line-height:1;padding:0 8px;height:25px;text-transform:capitalize;text-align:center;vertical-align:middle;cursor:pointer;color:#606c72;font-weight:700}
  2346. .sp-container button.sp-choose{background-color:#3cab3b;color:#fff;margin-left:5px}
  2347. .sp-container button:hover{opacity:.8}
  2348. .sp-container button.sp-palette-toggle{width:100%;background-color:#f3f3f3;margin:0}
  2349. .sp-palette span.sp-thumb-active,.sp-palette span:hover{border-color:#000}
  2350. .sp-alpha,.sp-preview,.sp-thumb-el{position:relative;background-image:url(${IMG_ICON_ALPHA})}
  2351. .sp-alpha-inner,.sp-preview-inner,.sp-thumb-inner{display:block;position:absolute;top:0;left:0;bottom:0;right:0}
  2352. .sp-palette .sp-thumb-inner{border-radius:3px;background-position:50% 50%;background-repeat:no-repeat}
  2353. .sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner{background-image:url(${IMG_ICON_CHECK_BLACK})}
  2354. .sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner{background-image:url(${IMG_ICON_CHECK_WHITE})}
  2355. .sp-clear-display{background-repeat:no-repeat;background-position:center;background-image:url(${IMG_ICON_BLOCK})}
  2356. `
  2357. }
  2358. /**
  2359. * 自动翻页模块
  2360. * @name AutoPage
  2361. * @description 此模块提供了脚本自动翻页的功能
  2362. * 提供一个高级配置可以设置自动翻页的检测阈值
  2363. */
  2364. const AutoPage = {
  2365. name: 'AutoPage',
  2366. title: '自动翻页',
  2367. settings: [{
  2368. type: 'normal',
  2369. key: 'autoPage',
  2370. default: true,
  2371. title: '自动翻页',
  2372. menu: 'right'
  2373. }],
  2374. $window: $(window),
  2375. initFunc() {
  2376. script.setting.normal.autoPage && $('body').addClass('hld__reply-fixed')
  2377. },
  2378. renderAlwaysFunc() {
  2379. const _this = this
  2380. if(script.setting.normal.autoPage) {
  2381. if($('#hld__next_page').length > 0) return
  2382. $('#pagebbtm>.stdbtn[hld-auto-page!=ok] td').each(function(){
  2383. if($(this).children('a').text() == '>') {
  2384. $(this).children('a').attr('id', 'hld__next_page')
  2385. _this.$window.on('scroll.autoPage', function(){
  2386. if ($(document).scrollTop() != 0 && (Math.ceil($(document).scrollTop()) + $(window).height() >= ($(document).height() - 20))) {
  2387. if($('#hld__next_page').length > 0) {
  2388. document.getElementById('hld__next_page').click()
  2389. $('#hld__next_page').removeAttr('id')
  2390. _this.$window.off('scroll.autoPage')
  2391. }
  2392. }
  2393. })
  2394. }
  2395. })
  2396. $('#pagebbtm>.stdbtn').attr('hld-auto-page', 'ok')
  2397. }
  2398. }
  2399. }
  2400. /**
  2401. * 关键字屏蔽模块
  2402. * @name KeywordsBlock
  2403. * @description 此模块提供了关键字屏蔽功能
  2404. * 提供一个高级配置可以设置是否过滤标题
  2405. */
  2406. const KeywordsBlock = {
  2407. name: 'KeywordsBlock',
  2408. title: '关键字屏蔽',
  2409. settings: [{
  2410. type: 'normal',
  2411. key: 'keywordsBlock',
  2412. default: true,
  2413. title: '关键字屏蔽',
  2414. menu: 'right',
  2415. extra: {
  2416. type: 'button',
  2417. label: '关键字管理',
  2418. id: 'hld__keywords_manage'
  2419. }
  2420. }, {
  2421. type: 'advanced',
  2422. key: 'kwdBlockContent',
  2423. default: 'ALL',
  2424. options: [{
  2425. label: '标题跟正文',
  2426. value: 'ALL'
  2427. }, {
  2428. label: '仅标题',
  2429. value: 'TITLE'
  2430. }, {
  2431. label: '仅正文',
  2432. value: 'BODY'
  2433. }],
  2434. title: '关键字屏蔽方式',
  2435. desc: '此配置表示关键字的屏蔽方式',
  2436. menu: 'right'
  2437. }],
  2438. keywordsList: [],
  2439. initFunc() {
  2440. const _this = this
  2441. // 同步本地数据
  2442. const localKeywordsList = script.getValue('hld__NGA_keywords_list')
  2443. try {
  2444. localKeywordsList && (_this.keywordsList = JSON.parse(localKeywordsList))
  2445. } catch {
  2446. localKeywordsList && (_this.keywordsList = localKeywordsList.split(','))
  2447. script.setValue('hld__NGA_keywords_list', JSON.stringify(_this.keywordsList))
  2448. }
  2449. // 添加到导入导出配置
  2450. script.getModule('BackupModule').addItem({
  2451. title: '关键字列表',
  2452. writeKey: 'keywords_list',
  2453. valueKey: 'keywordsList',
  2454. module: this
  2455. })
  2456. /**
  2457. * Bind:Click
  2458. * 管理弹窗面板
  2459. */
  2460. $('body').on('click', '#hld__keywords_manage', function () {
  2461. if($('#hld__keywords_panel').length > 0) return
  2462. $('#hld__setting_cover').append(`<div id="hld__keywords_panel" class="hld__list-panel animated fadeInUp">
  2463. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  2464. <div>
  2465. <div class="hld__list-c"><p>屏蔽关键字</p><textarea row="20" id="hld__keywords_list_textarea"></textarea><p class="hld__list-desc">一行一条</p></div>
  2466. </div>
  2467. <div class="hld__btn-groups"><button class="hld__btn" id="hld__save_keywords">保存列表</button></div>
  2468. </div>`)
  2469. $('#hld__keywords_list_textarea').val(_this.keywordsList.join('\n'))
  2470. })
  2471. /**
  2472. * Bind:Click
  2473. * 保存关键字
  2474. */
  2475. $('body').on('click', '#hld__save_keywords', function () {
  2476. let keywordsList = $('#hld__keywords_list_textarea').val().split('\n')
  2477. keywordsList = _this.removeBlank(keywordsList)
  2478. keywordsList = _this.uniq(keywordsList)
  2479. _this.keywordsList = keywordsList
  2480. script.setValue('hld__NGA_keywords_list', JSON.stringify(_this.keywordsList))
  2481. $('#hld__keywords_panel').remove()
  2482. script.popMsg('保存成功,刷新页面生效')
  2483. })
  2484. },
  2485. renderThreadsFunc($el) {
  2486. const title = $el.find('.c2>a').text()
  2487. if ((script.setting.advanced.kwdBlockContent === 'ALL' || script.setting.advanced.kwdBlockContent === 'TITLE') && script.setting.normal.keywordsBlock && this.keywordsList.length > 0) {
  2488. for (let keyword of this.keywordsList) {
  2489. if (title.includes(keyword)) {
  2490. script.printLog(`关键字屏蔽: 标题: ${title} 连接: ${$el.find('.c2>a').attr('href')}`)
  2491. $el.remove()
  2492. break
  2493. }
  2494. }
  2495. }
  2496. },
  2497. renderFormsFunc($el) {
  2498. const _this = this
  2499. if (script.setting.normal.keywordsBlock && this.keywordsList.length > 0 && (script.setting.advanced.kwdBlockContent === 'ALL' || script.setting.advanced.kwdBlockContent === 'BODY')) {
  2500. const $postcontent = $el.find('.postcontent')
  2501. const $postcontentClone = $postcontent.clone()
  2502. const consoleLog = (text) => script.printLog(`关键字屏蔽: 内容: ${text}`)
  2503. let postcontentQuote = ''
  2504. let postcontentText = ''
  2505.  
  2506. if ($postcontent.find('.quote').length > 0) {
  2507. $postcontentClone.find('.quote').remove()
  2508. let postcontentText = $postcontent.find('.quote').text()
  2509. const endIndex = postcontentText.indexOf(')')
  2510. postcontentQuote = postcontentText.substring(endIndex + 1)
  2511. }
  2512.  
  2513. postcontentText = $postcontentClone.text()
  2514. let blockCount = 0
  2515. for (let keyword of this.keywordsList) {
  2516. if (postcontentText && postcontentText.includes(keyword)) {
  2517. consoleLog(postcontentText)
  2518. $el.remove()
  2519. blockCount += 1
  2520. break
  2521. }
  2522. if (postcontentQuote && postcontentQuote.includes(keyword)) {
  2523. consoleLog(postcontentQuote)
  2524. blockCount += 1
  2525. $postcontent.find('.quote').remove()
  2526. }
  2527. }
  2528. const $commentCList = $el.find('.comment_c')
  2529. if ($commentCList.length > 0) {
  2530. let postcontentReply = ''
  2531. $commentCList.each(function () {
  2532. let postcontentReplyText = $el.find('.ubbcode').text()
  2533. const end_index = postcontentReplyText.indexOf(')')
  2534. postcontentReply = postcontentReplyText.substring(end_index + 1)
  2535. for (let keyword of _this.keywordsList) {
  2536. if (postcontentReply && postcontentReply.includes(keyword)) {
  2537. consoleLog(postcontentReply)
  2538. blockCount += 1
  2539. $(this).remove()
  2540. }
  2541. }
  2542. })
  2543. }
  2544. }
  2545. },
  2546. /**
  2547. * 列表去空
  2548. * @method removeBlank
  2549. * @param {Array} array 列表
  2550. * @return {Array} 处理后的列表
  2551. */
  2552. removeBlank(array) {
  2553. let r = []
  2554. array.map(function (val, index) {
  2555. if (val !== '' && val != undefined) {
  2556. r.push(val)
  2557. }
  2558. });
  2559. return r
  2560. },
  2561. /**
  2562. * 列表去重
  2563. * @method uniq
  2564. * @param {Array} array 列表
  2565. * @return {Array} 处理后的列表
  2566. */
  2567. uniq(array) {
  2568. return [...new Set(array)]
  2569. },
  2570. style: `
  2571. #hld__keywords_panel {width:182px;}
  2572. #hld__keywords_panel .hld__list-c {width:100%;}
  2573. `
  2574. }
  2575. /**
  2576. * 黑名单标记模块
  2577. * @name MarkAndBan
  2578. * @description 此模块提供了黑名单屏蔽功能,标签标记功能
  2579. * 提供高级配置可以设置标记/备注风格
  2580. * 提供高级配置可以设置功能面板显示方式
  2581. * 提供高级配置可以设置黑名单屏蔽策略
  2582. */
  2583. const MarkAndBan = {
  2584. name: 'MarkAndBan',
  2585. title: '黑名单标记',
  2586. settings: [{
  2587. type: 'normal',
  2588. key: 'markAndBan',
  2589. default: true,
  2590. title: '拉黑/标签功能',
  2591. menu: 'right',
  2592. extra: {
  2593. type: 'button',
  2594. label: '名单管理',
  2595. id: 'hld__list_manage'
  2596. }
  2597. }, {
  2598. type: 'advanced',
  2599. key: 'classicRemark',
  2600. default: false,
  2601. title: '经典备注风格',
  2602. desc: '此配置表示标记功能的风格显示\n选中时: v2.9及以前的备注风格(仿微博),此风格不能更改颜色\n取消时: 新版标记风格',
  2603. menu: 'right'
  2604. }, {
  2605. type: 'advanced',
  2606. key: 'autoHideBanIcon',
  2607. default: false,
  2608. title: '按需显示标注拉黑按钮',
  2609. desc: '选中时: 默认隐藏标注与拉黑按钮, 当鼠标停留区域时, 才会显示\n取消时: 一直显示',
  2610. menu: 'right'
  2611. }, {
  2612. type: 'advanced',
  2613. key: 'banStrictMode',
  2614. default: 'HIDE',
  2615. options: [{
  2616. label: '屏蔽',
  2617. value: 'HIDE'
  2618. }, {
  2619. label: '删除',
  2620. value: 'REMOVE'
  2621. }, {
  2622. label: '全部删除',
  2623. value: 'ALL'
  2624. }],
  2625. title: '拉黑模式',
  2626. desc: '此配置表示拉黑某人后对帖子的屏蔽策略\n屏蔽: 保留楼层, 仅会屏蔽用户的回复\n删除: 将会删除楼层\n全部删除: 回复被拉黑用户的回复也会被删除',
  2627. menu: 'right'
  2628. }],
  2629. banList: [],
  2630. markList: [],
  2631. markedTags: [],
  2632. initFunc() {
  2633. const _this = this
  2634. // 读取本地数据
  2635. const localBanList = script.getValue('hld__NGA_ban_list')
  2636. try {
  2637. localBanList && (_this.banList = JSON.parse(localBanList))
  2638. } catch(e) {
  2639. script.throwError(`【NGA-Script】无法加载黑名单列表,数据解析失败!\n错误问题: ${e}\n\n请尝试使用【修复脚本】来修复此问题`)
  2640. }
  2641. const localMarkList = script.getValue('hld__NGA_mark_list')
  2642. try {
  2643. if (localMarkList) {
  2644. _this.markList = JSON.parse(localMarkList)
  2645. // 统计已添加过的标签
  2646. _this.markList.forEach(item => {
  2647. item.marks.forEach(mark => {
  2648. const exist_tag = _this.markedTags.find(t => t.mark == mark.mark)
  2649. if (exist_tag) {
  2650. exist_tag.count += 1
  2651. } else {
  2652. _this.markedTags.push({
  2653. mark: mark.mark,
  2654. text_color: mark.text_color,
  2655. bg_color: mark.bg_color,
  2656. count: 1
  2657. })
  2658. }
  2659. })
  2660. })
  2661. _this.markedTags.sort((a, b) => {return b.count - a.count})
  2662. }
  2663. } catch(e) {
  2664. script.throwError(`【NGA-Script】无法加载标记列表,数据解析失败!\n错误问题: ${e}\n\n请尝试使用【修复脚本】来修复此问题`)
  2665. }
  2666. // 添加到导入导出配置
  2667. script.getModule('BackupModule').addItem({
  2668. title: '黑名单列表',
  2669. writeKey: 'ban_list',
  2670. valueKey: 'banList',
  2671. module: this
  2672. })
  2673. script.getModule('BackupModule').addItem({
  2674. title: '标记名单列表',
  2675. writeKey: 'mark_list',
  2676. valueKey: 'markList',
  2677. module: this
  2678. })
  2679. // 拉黑标签-名单
  2680. if (script.setting.normal.markAndBan) {
  2681. /**
  2682. * Bind:Click
  2683. * 操作按钮点击事件
  2684. */
  2685. $('body').on('click', '.hld__extra-icon', function () {
  2686. const type = $(this).data('type')
  2687. const name = $(this).data('name')
  2688. const uid = $(this).data('uid') + ''
  2689. $('.hld__dialog').length > 0 && $('.hld__dialog').remove()
  2690. if (type == 'ban') {
  2691. _this.banlistPopup({
  2692. type: 'confirm',
  2693. name,
  2694. uid,
  2695. top: $(this).offset().top+20,
  2696. left: $(this).offset().left-10
  2697. })
  2698. }
  2699. if (type == 'mark') {
  2700. _this.userMarkPopup({
  2701. name,
  2702. uid,
  2703. top: $(this).offset().top+20,
  2704. left: $(this).offset().left-10
  2705. })
  2706. }
  2707. })
  2708. /**
  2709. * Bind:Click
  2710. * 屏蔽按钮
  2711. */
  2712. $('body').on('click', '.hld__banned-block', function(){
  2713. if ($(this).parent().hasClass('quote')) {
  2714. $(this).parent().prev().show()
  2715. $(this).parent().hide()
  2716. } else {
  2717. $(this).prev().show()
  2718. $(this).hide()
  2719. }
  2720. })
  2721. /**
  2722. * Bind:Click
  2723. * 名单管理
  2724. */
  2725. $('body').on('click', '#hld__list_manage', function () {
  2726. if($('#hld__banlist_panel').length > 0) return
  2727. $('#hld__setting_cover').append(`<div id="hld__banlist_panel" class="hld__list-panel animated fadeInUp">
  2728. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  2729. <div class="hld__tab-header"><span class="hld__table-active">简易模式</span><span>原始数据</span></div>
  2730. <div class="hld__tab-content hld__format-list hld__table-active">
  2731. <div class="hld__list-c"><p>黑名单</p>
  2732. <div class="hld__scroll-area">
  2733. <table class="hld__table hld__table-banlist">
  2734. <thead><tr><th width="175">用户名</th><th>UID</th><th>备注</th><th width="25">操作</th></tr></thead><tbody id="hld__banlist"></tbody></table>
  2735. </div>
  2736. <div class="hld__table-banlist-buttons"><button id="hld__banlist_add_btn" class="hld__btn">+添加用户</button></div>
  2737. </div>
  2738. <div class="hld__list-c"><p>标签名单</p>
  2739. <div class="hld__scroll-area">
  2740. <table class="hld__table hld__table-banlist">
  2741. <thead><tr><th width="100">用户名</th><th>UID</th><th width="50">标签数</th><th width="50">操作</th></tr></thead><tbody id="hld__marklist"></tbody></table>
  2742. </div>
  2743. <div class="hld__table-banlist-buttons"><button id="hld__marklist_add_btn" class="hld__btn">+添加用户</button></div>
  2744. </div>
  2745. </div>
  2746. <div class="hld__tab-content hld__source-list">
  2747. <div class="hld__list-c"><p>黑名单</p><textarea row="20" id="hld__ban_list_textarea"></textarea><p class="hld__list-desc" title='[{\n "uid": "UID",\n "name": "用户名"\n }, ...]'>查看数据结构</p></div>
  2748. <div class="hld__list-c"><p>标签名单</p><textarea row="20" id="hld__mark_list_textarea"></textarea><p class="hld__list-desc" title='[{\n "uid": "UID",\n "name": "用户名",\n "marks": [{\n "mark": "标记",\n "text_color": "文字色",\n "bg_color": "背景色"\n }, ...]\n }, ...]'>查看数据结构</p></div>
  2749. <div class="hld__btn-groups" style="width: 100%;"><button class="hld__btn" id="hld__save_banlist">保存列表</button></div>
  2750. </div>
  2751. </div>`)
  2752. /**
  2753. * Bind:Click
  2754. * 切换选项卡
  2755. */
  2756. $('body').on('click', '.hld__tab-header > span', function(){
  2757. $('.hld__tab-header > span, .hld__tab-content').removeClass('hld__table-active')
  2758. $(this).addClass('hld__table-active')
  2759. $('.hld__tab-content').eq($(this).index()).addClass('hld__table-active')
  2760. })
  2761. /**
  2762. * Bind:Click
  2763. * 移除黑名单
  2764. */
  2765. $('body').on('click', '.hld__bl-del', function(){
  2766. const index = $(this).data('index')
  2767. _this.banList.splice(index, 1)
  2768. script.setValue('hld__NGA_ban_list', JSON.stringify(_this.banList))
  2769. _this.reloadBanlist()
  2770. })
  2771. /**
  2772. * Bind:Click
  2773. * 添加黑名单
  2774. */
  2775. $('body').on('click', '#hld__banlist_add_btn', function(){
  2776. _this.banlistPopup({
  2777. type: 'add',
  2778. name: $(this).data('name'),
  2779. uid: $(this).data('uid'),
  2780. top: $(this).offset().top + 30,
  2781. left: $(this).offset().left - 5,
  2782. callback: () => {_this.reloadBanlist()}
  2783. })
  2784. })
  2785. /**
  2786. * Bind:Click
  2787. * 保存黑名单
  2788. */
  2789. $('body').on('click', '#hld__save_banlist', function(){
  2790. const banList = $('#hld__ban_list_textarea').val()
  2791. const markList = $('#hld__mark_list_textarea').val()
  2792. try {
  2793. _this.banList = JSON.parse(banList)
  2794. script.setValue('hld__NGA_ban_list', banList)
  2795. _this.reloadBanlist()
  2796. } catch {
  2797. script.popMsg('黑名单数据有误!', 'err')
  2798. return
  2799. }
  2800. try {
  2801. _this.markList = JSON.parse(markList)
  2802. script.setValue('hld__NGA_mark_list', markList)
  2803. _this.reloadMarklist()
  2804. } catch {
  2805. script.popMsg('标记单数据有误!', 'err')
  2806. return
  2807. }
  2808. script.popMsg('数据已成功')
  2809. })
  2810. /**
  2811. * Bind:Click
  2812. * 修改标记
  2813. */
  2814. $('body').on('click', '.hld__ml-edit', function(){
  2815. const name = $(this).data('name')
  2816. const uid = $(this).data('uid') + ''
  2817. _this.userMarkPopup({
  2818. name,
  2819. uid,
  2820. top: $(this).offset().top + 30,
  2821. left: $(this).offset().left - 5,
  2822. callback: () => {_this.reloadMarklist()}
  2823. })
  2824. })
  2825. /**
  2826. * Bind:Click
  2827. * 删除标记
  2828. */
  2829. $('body').on('click', '.hld__ml-del', function(){
  2830. const index = $(this).data('index')
  2831. _this.markList.splice(index, 1)
  2832. script.setValue('hld__NGA_mark_list', JSON.stringify(_this.markList))
  2833. _this.reloadMarklist()
  2834. })
  2835. /**
  2836. * Bind:Click
  2837. * 添加标记
  2838. */
  2839. $('body').on('click', '#hld__marklist_add_btn', function(){
  2840. _this.userMarkPopup({
  2841. type: 'add',
  2842. name: $(this).data('name'),
  2843. uid: $(this).data('uid'),
  2844. top: $(this).offset().top + 30,
  2845. left: $(this).offset().left - 5,
  2846. callback: () => {_this.reloadMarklist()}
  2847. })
  2848. })
  2849. //重载名单
  2850. _this.reloadBanlist()
  2851. _this.reloadMarklist()
  2852. })
  2853. }
  2854. },
  2855. renderThreadsFunc($el) {
  2856. const title = $el.find('.c2>a').text()
  2857. const uid = ($el.find('.author').attr('href') && $el.find('.author').attr('href').indexOf('uid=') > -1) ? $el.find('.author').attr('href').split('uid=')[1] + '' : ''
  2858. const name = $el.find('.author').text()
  2859. if (script.setting.normal.markAndBan) {
  2860. const banUser = this.getBanUser({name, uid})
  2861. //黑名单屏蔽
  2862. if (this.banList.length > 0 && banUser) {
  2863. script.printLog(`黑名单屏蔽: 标题: ${title} 连接: ${$el.find('.c2>a').attr('href')}`)
  2864. $el.parents('tbody').remove()
  2865. }
  2866. }
  2867. },
  2868. renderFormsFunc($el) {
  2869. const _this = this
  2870. if (script.setting.normal.markAndBan) {
  2871. // 插入操作面板
  2872. const currentUid = $el.find('[name=uid]').text() + ''
  2873. $el.find('.small_colored_text_btn.block_txt_c2.stxt').each(function () {
  2874. let currentName = ''
  2875. if ($(this).parents('td').prev('td').html() == '') {
  2876. currentName = $(this).parents('table').prev('.posterinfo').children('.author').text()
  2877. } else {
  2878. currentName = $(this).parents('td').prev('td').find('.author').text()
  2879. }
  2880. currentName.endsWith('楼主') && (currentName = currentName.substring(0, currentName.length - 2))
  2881. const mbDom = `
  2882. <a class="hld__extra-icon hld__help" data-type="mark" help="标签此用户" data-name="${currentName}" data-uid="${currentUid}">🏷️</a>
  2883. <a class="hld__extra-icon hld__help" help="拉黑此用户(屏蔽所有言论)" data-type="ban" data-name="${currentName}" data-uid="${currentUid}">⛔</a>
  2884. `
  2885. script.setting.advanced.autoHideBanIcon ? $(this).after(`<span class="hld__extra-icon-box">${mbDom}</span>`) : $(this).append(mbDom)
  2886. })
  2887. // 标记DOm
  2888. $el.find('a.b').each(function () {
  2889. const uid = ($(this).attr('href') && $(this).attr('href').indexOf('uid=') > -1) ? $(this).attr('href').split('uid=')[1] + '' : ''
  2890. let name = ''
  2891. if ($(this).find('span.hld__post-author').length > 0 || $(this).find('span.hld__remark').length > 0) {
  2892. const $a = $(this).clone()
  2893. $a.find('span.hld__post-author, span.hld__remark').remove()
  2894. name = $a.text()
  2895. } else {
  2896. name = $(this).attr('hld-mark-before-name') || $(this).text().replace('[', '').replace(']', '')
  2897. }
  2898. const banUser = _this.getBanUser({name, uid})
  2899. if (banUser) {
  2900. //拉黑用户实现
  2901. if (script.setting.advanced.banStrictMode == 'HIDE') {
  2902. if ($(this).hasClass('author')) {
  2903. const $blocktips = $('<div class="hld__banned hld__banned-block">此用户在你的黑名单中,已屏蔽其言论,点击查看</div>')
  2904. if ($(this).parents('div.comment_c').length > 0) {
  2905. $(this).parents('div.comment_c').find('.ubbcode').hide()
  2906. $(this).parents('div.comment_c').find('.ubbcode').after($blocktips)
  2907. } else {
  2908. $(this).parents('.forumbox.postbox').find('.c2 .postcontent').hide()
  2909. $(this).parents('.forumbox.postbox').find('.c2 .postcontent').after($blocktips)
  2910. }
  2911. } else {
  2912. if (!$(this).parent().is(':hidden')) {
  2913. $(this).parent().hide()
  2914. $(this).parent().after('<div class="quote"><div class="hld__banned hld__banned-block">此用户在你的黑名单中,已屏蔽其言论,点击查看</div></div>')
  2915. }
  2916. }
  2917. } else if (script.setting.advanced.banStrictMode == 'ALL') {
  2918. if ($(this).parents('div.comment_c').length > 0) $(this).parents('div.comment_c').remove()
  2919. else $(this).parents('.forumbox.postbox').remove()
  2920. } else {
  2921. if ($(this).hasClass('author')) {
  2922. if ($(this).parents('div.comment_c').length > 0) $(this).parents('div.comment_c').remove()
  2923. else $(this).parents('.forumbox.postbox').remove()
  2924. } else {
  2925. $(this).parent().html('<div class="hld__banned">此用户在你的黑名单中,已删除其言论</div>')
  2926. }
  2927. }
  2928. if (banUser.desc) {
  2929. $(this).parents('.postrow').find('.hld__banned').append(`<div>备注: ${banUser.desc}</div>`)
  2930. }
  2931. script.printLog(`黑名单屏蔽: 用户: ${name}, UID:${uid}, 备注:${banUser.desc}`)
  2932. }
  2933. if(script.setting.advanced.classicRemark) {
  2934. //经典备注风格
  2935. const userMarks = _this.getUserMarks({name, uid})
  2936. if (userMarks) {
  2937. let f = []
  2938. userMarks.marks.forEach(e => f.push(e.mark))
  2939. $(this).attr('hld-mark-before-name', name).append(`<span class="hld__remark"> (${f.join(', ')}) </span>`)
  2940. }
  2941. }else {
  2942. //新版标签风格
  2943. const userMarks = _this.getUserMarks({name, uid})
  2944. if(userMarks) {
  2945. const $el = $(this).parents('.c1').find('.clickextend')
  2946. let marksDom = ''
  2947. userMarks.marks.forEach(item => marksDom += `<span ${item.desc ? 'class="hld__help" help="'+item.desc+'"' : ''} style="color: ${item.text_color};background-color: ${item.bg_color};">${item.mark}</span>`);
  2948. $el.before(`<div class="hld__marks-container">标签: ${marksDom}</div>`)
  2949. }
  2950. }
  2951. })
  2952. }
  2953. },
  2954. /**
  2955. * 黑名单弹窗
  2956. * @method banlistPopup
  2957. * @param {Object} setting 设置项
  2958. * @param {String} setting.name 用户昵称
  2959. * @param {String} setting.uid UID
  2960. * @param {String} setting.type 模式
  2961. * @param {Number} setting.top pos.top位置
  2962. * @param {Number} setting.left pos.left 位置
  2963. * @param {Function} setting.callback 回调函数
  2964. */
  2965. banlistPopup(setting) {
  2966. const _this = this
  2967. $('.hld__dialog').length > 0 && $('.hld__dialog').remove()
  2968. let $banDialog = $(`<div class="hld__dialog hld__dialog-sub-top hld__list-panel animated zoomIn" style="top: ${setting.top}px;left: ${setting.left}px;"><a href="javascript:void(0)" class="hld__setting-close">×</a><div id="container_dom"></div><div class="hld__dialog-buttons"></div></div>`)
  2969. if (setting.type == 'confirm') {
  2970. $banDialog.find('#container_dom').append(`<div><span>您确定要拉黑用户</span><span class="hld__dialog-user">${setting.name}</span><span>吗?</span></div><div><input type="text" class="hld__ban-desc" placeholder="可选备注"></div>`)
  2971. let $okBtn = $('<button class="hld__btn">拉黑</button>')
  2972. $okBtn.click(function(){
  2973. _this.setBanUser({
  2974. name: setting.name,
  2975. uid: setting.uid,
  2976. desc: $('.hld__ban-desc').val().trim()
  2977. })
  2978. $('.hld__dialog').remove()
  2979. script.popMsg('拉黑成功,重载页面生效')
  2980. })
  2981. $banDialog.find('.hld__dialog-buttons').append($okBtn)
  2982. }else if (setting.type == 'add') {
  2983. $banDialog.find('#container_dom').append(`<div>添加用户: </div><div><input id="hld__dialog_add_uid" type="text" value="" placeholder="UID"></div><div><input id="hld__dialog_add_name" type="text" value="" placeholder="用户名"></div><div><input type="text" id="hld__dialog_add_desc" placeholder="可选备注"></div>`)
  2984. let $okBtn = $('<button class="hld__btn">添加</button>')
  2985. $okBtn.click(function(){
  2986. const name = $banDialog.find('#hld__dialog_add_name').val().trim()
  2987. const uid = $banDialog.find('#hld__dialog_add_uid').val().trim() + ''
  2988. const desc = $banDialog.find('#hld__dialog_add_desc').val().trim()
  2989. if (!name && !uid) {
  2990. script.popMsg('UID与用户名必填一个,其中UID权重较大', 'err')
  2991. return
  2992. }
  2993. !_this.getBanUser({name, uid}) && _this.banList.push({name, uid, desc})
  2994. script.setValue('hld__NGA_ban_list', JSON.stringify(_this.banList))
  2995. $('.hld__dialog').remove()
  2996. setting.callback()
  2997. })
  2998. $banDialog.find('.hld__dialog-buttons').append($okBtn)
  2999. }
  3000. $('body').append($banDialog)
  3001. },
  3002. /**
  3003. * 获取黑名单用户
  3004. * @method getBanUser
  3005. * @param {Object} banObj 黑名单对象
  3006. * @return {Object|null} 获取的用户对象
  3007. */
  3008. getBanUser(banObj) {
  3009. const _this = this
  3010. for (let u of _this.banList) {
  3011. if ((u.uid && banObj.uid && u.uid == banObj.uid) ||
  3012. (u.name && banObj.name && u.name == banObj.name)) {
  3013. if ((!u.uid && banObj.uid) || (!u.name && banObj.name)) {
  3014. u.uid = banObj.uid + '' || ''
  3015. u.name = banObj.name || ''
  3016. script.setValue('hld__NGA_ban_list', JSON.stringify(_this.banList))
  3017. }
  3018. return u
  3019. }
  3020. }
  3021. return null
  3022. },
  3023. /**
  3024. * 拉黑用户
  3025. * @method setBanUser
  3026. * @param {Object} banObj 黑名单对象, {uid, name}
  3027. */
  3028. setBanUser(banObj) {
  3029. !this.getBanUser(banObj) && this.banList.push(banObj)
  3030. script.setValue('hld__NGA_ban_list', JSON.stringify(this.banList))
  3031. },
  3032. /**
  3033. * 重新渲染黑名单列表
  3034. * @method reloadBanlist
  3035. */
  3036. reloadBanlist() {
  3037. const _this = this
  3038. $('#hld__banlist').empty()
  3039. _this.banList.forEach((item, index) => $('#hld__banlist').append(`
  3040. <tr>
  3041. <td title="${item.name}">${item.name}</td>
  3042. <td title="${item.uid}">${item.uid}</td>
  3043. <td title="${item.desc}">${item.desc || ''}</td>
  3044. <td>
  3045. <span class="hld__us-action hld__us-del hld__bl-del" title="删除" data-index="${index}" data-name="${item.name}" data-uid="${item.uid}">
  3046. <svg t="1686881304570" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2976" width="48" height="48"><path d="M341.312 85.312l64-85.312h213.376l64 85.312H960v85.376H64V85.312h277.312zM170.688 256h682.624v768H170.688V256zM256 341.312v597.376h512V341.312H256z m213.312 85.376v426.624H384V426.688h85.312z m170.688 0v426.624H554.688V426.688H640z" fill="#111111" p-id="2977"></path></svg>
  3047. </span>
  3048. </td>
  3049. </tr>
  3050. `))
  3051. $('#hld__ban_list_textarea').val(JSON.stringify(_this.banList))
  3052. },
  3053. /**
  3054. * 重新渲染标签列表
  3055. * @method reloadMarklist
  3056. */
  3057. reloadMarklist() {
  3058. const _this = this
  3059. $('#hld__marklist').empty()
  3060. _this.markList.forEach((user_mark, index) => {
  3061. $('#hld__marklist').append(`
  3062. <tr>
  3063. <td title="${user_mark.name}">${user_mark.name}</td>
  3064. <td title="${user_mark.uid}">${user_mark.uid}</td>
  3065. <td title="${user_mark.marks.length}">${user_mark.marks.length}</td>
  3066. <td>
  3067. <span class="hld__us-action hld__us-edit hld__ml-edit" title="编辑" data-index="${index}" data-name="${user_mark.name}" data-uid="${user_mark.uid}">
  3068. <svg t="1686881523486" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4234" width="48" height="48"><path d="M652.4 156.6125a112.5 112.5 0 1 1 155.925 161.15625L731.375 394.71875 572.3 235.5875l79.5375-79.5375 0.5625 0.5625zM333.63125 792.40625v0.1125H174.5v-159.1875l358.03125-357.975 159.075 159.13125-357.975 357.91875zM62 849.5h900v112.5H62v-112.5z" fill="#111111" p-id="4235"></path></svg>
  3069. </span>
  3070. <span class="hld__us-action hld__us-del hld__ml-del" title="删除" data-index="${index}" data-name="${user_mark.name}" data-uid="${user_mark.uid}">
  3071. <svg t="1686881304570" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2976" width="48" height="48"><path d="M341.312 85.312l64-85.312h213.376l64 85.312H960v85.376H64V85.312h277.312zM170.688 256h682.624v768H170.688V256zM256 341.312v597.376h512V341.312H256z m213.312 85.376v426.624H384V426.688h85.312z m170.688 0v426.624H554.688V426.688H640z" fill="#111111" p-id="2977"></path></svg>
  3072. </span>
  3073. </td>
  3074. </tr>
  3075. `)
  3076. })
  3077. $('#hld__mark_list_textarea').val(JSON.stringify(_this.markList))
  3078. },
  3079. /**
  3080. * 标记弹窗
  3081. * @method userMarkPopup
  3082. * @param {Object} setting 设置项
  3083. * @param {String} setting.name 用户名
  3084. * @param {String} setting.uid UID
  3085. * @param {String} setting.type 模式
  3086. * @param {Number} setting.top pos.top位置
  3087. * @param {Number} setting.left pos.left 位置
  3088. * @param {Function} setting.callback 回调函数
  3089. */
  3090. userMarkPopup(setting) {
  3091. const _this = this
  3092. $('.hld__dialog').length > 0 && $('.hld__dialog').remove()
  3093. let $markDialog = $(`<div class="hld__dialog hld__dialog-sub-top hld__list-panel animated zoomIn" style="top: ${setting.top}px;left: ${setting.left}px;">
  3094. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  3095. ${setting.type == 'add' ? `<div style="display:block;">添加用户: <input id="hld__dialog_add_uid" type="text" value="" placeholder="UID"><input id="hld__dialog_add_name" type="text" value="" placeholder="用户名"></div>` : ''}
  3096. <table class="hld__dialog-mark-table">
  3097. <thead>
  3098. <tr>
  3099. <th width="100">标签</th><th width="50">文字</th><th width="50">背景</th><th>备注</th><th>操作</th>
  3100. </tr>
  3101. </thead>
  3102. <tbody id="hld__mark_body"></tbody>
  3103. </table>
  3104. <div class="hld__dialog-buttons hld__button-insert" style="justify-content: left !important;"></div>
  3105. <div class="hld__mark_history"><div class="hld__mark_history-title">选择已添加过的标签</div><div class="hld__mark_history-content"><div class="hld__mark_history-scrollarea">暂无</div></div></div>
  3106. <div class="hld__dialog-buttons hld__button-save" style="justify-content: right !important;"></div>
  3107. </div>`)
  3108. const insertRemarkRow = (r='', t='#ffffff', b='#1f72f1', d='', n=true) => {
  3109. let $tr = $(`<tr>
  3110. <td><input type="text" class="hld__mark-mark" value="${r}"></td>
  3111. <td><input class="hld__dialog-color-picker hld__mark-text-color" value="${t}"></td>
  3112. <td><input class="hld__dialog-color-picker hld__mark-bg-color" value="${b}"></td>
  3113. <td><textarea rows="1" class="hld__mark-desc"/></td>
  3114. <td><button title="删除此标签" class="hld__mark-del">x</button></td>
  3115. </tr>`)
  3116. $tr.find('.hld__mark-del').click(function(){$(this).parents('tr').remove()})
  3117. $tr.find('.hld__mark-desc').val(d)
  3118. script.getModule('AuthorMark').initSpectrum($tr.find('.hld__dialog-color-picker'))
  3119. $markDialog.find('#hld__mark_body').append($tr)
  3120. n && $tr.find('.hld__mark-mark').focus()
  3121. }
  3122.  
  3123. _this.markedTags.length > 0 && $markDialog.find('.hld__mark_history-scrollarea').empty()
  3124. _this.markedTags.forEach(tag => {
  3125. $markDialog.find('.hld__mark_history-scrollarea').append(`
  3126. <span title="${tag.mark}" textcolor="${tag.text_color}" bgcolor="${tag.bg_color}" desc="${tag.desc}" style="color: ${tag.text_color};background-color: ${tag.bg_color};">${tag.mark} (${tag.count})</span>
  3127. `)
  3128. })
  3129. $markDialog.on('click', '.hld__mark_history-scrollarea > span', function (e) {
  3130. insertRemarkRow($(this).attr('title'), $(this).attr('textcolor'), $(this).attr('bgcolor'), '', false)
  3131. })
  3132.  
  3133. //恢复标签
  3134. const existMark = _this.getUserMarks({name: setting.name, uid: setting.uid})
  3135. existMark !== null && existMark.marks.forEach(item => insertRemarkRow(item.mark, item.text_color, item.bg_color, item.desc, false))
  3136.  
  3137. let $addBtn = $('<button class="hld__btn">+添加新标签</button>')
  3138. $addBtn.click(() => insertRemarkRow())
  3139. $markDialog.find('.hld__button-insert').append($addBtn)
  3140. let $okBtn = $('<button class="hld__btn">保存</button>')
  3141.  
  3142. $okBtn.click(function(){
  3143. let userMarks = {marks: []}
  3144. if (setting.type == 'add') {
  3145. userMarks.name = $markDialog.find('#hld__dialog_add_name').val().trim()
  3146. userMarks.uid = $markDialog.find('#hld__dialog_add_uid').val().trim() + ''
  3147. } else {
  3148. userMarks.name = setting.name
  3149. userMarks.uid = setting.uid + ''
  3150. }
  3151. if (!userMarks.name && !userMarks.uid) {
  3152. script.popMsg('UID与用户名必填一个,其中UID权重较大', 'err')
  3153. return
  3154. }
  3155. $('#hld__mark_body > tr').each(function(){
  3156. const mark = $(this).find('.hld__mark-mark').val().trim()
  3157. const textColor = $(this).find('.hld__mark-text-color').val()
  3158. const bgColor = $(this).find('.hld__mark-bg-color').val()
  3159. const desc = $(this).find('.hld__mark-desc').val().trim()
  3160. if(mark) {
  3161. userMarks.marks.push({mark, text_color: textColor, bg_color: bgColor, desc})
  3162. }
  3163. })
  3164. if (setting.type == 'add' && userMarks.marks.length == 0) {
  3165. script.popMsg('至少添加一个标签内容', 'err')
  3166. return
  3167. }
  3168. _this.setUserMarks(userMarks)
  3169. script.popMsg('保存成功,重载页面生效')
  3170. $('.hld__dialog').remove()
  3171. setting.callback()
  3172. })
  3173. $markDialog.find('.hld__button-save').append($okBtn)
  3174. $('body').append($markDialog)
  3175. script.getModule('AuthorMark').initSpectrum('.hld__dialog-color-picker')
  3176. },
  3177. /**
  3178. * 获取用户标签对象
  3179. * @method getUserMarks
  3180. * @param {String} uid UID
  3181. * @param {String} user 用户名
  3182. * @return {Object|null} 标签对象
  3183. */
  3184. getUserMarks(user) {
  3185. const check = this.markList.findIndex(v => (v.uid && user.uid && v.uid == user.uid) || (v.name && user.name && v.name == user.name))
  3186. if(check > -1) {
  3187. let userMark = this.markList[check]
  3188. if ((!userMark.uid && user.uid) || (!userMark.name && user.name)) {
  3189. userMark.uid = user.uid + '' || ''
  3190. userMark.name = user.name || ''
  3191. script.setValue('hld__NGA_mark_list', JSON.stringify(this.markList))
  3192. }
  3193. return userMark
  3194. } else {
  3195. return null
  3196. }
  3197. },
  3198. /**
  3199. * 保存标签
  3200. * @method setUserMarks
  3201. * @param {Object} userMarks 标签对象
  3202. */
  3203. setUserMarks(userMarks) {
  3204. // 检查是否已有标签
  3205. const _this = this
  3206. const check = _this.markList.findIndex(v => (v.uid && userMarks.uid && v.uid == userMarks.uid) ||
  3207. (v.name && userMarks.name && v.name == userMarks.name))
  3208. if(check > -1) {
  3209. if (userMarks.marks.length == 0) {
  3210. _this.markList.splice(check, 1)
  3211. } else {
  3212. _this.markList[check] = userMarks
  3213. }
  3214. }else {
  3215. _this.markList.push(userMarks)
  3216. }
  3217. script.setValue('hld__NGA_mark_list', JSON.stringify(_this.markList))
  3218. },
  3219. style: `
  3220. #hld__setting {color:#6666CC !important;cursor:pointer;}
  3221. .hld__list-panel {position:fixed;background:#fff8e7;padding:15px 20px;border-radius:10px;box-shadow:0 0 10px #666;border:1px solid #591804;z-index:9999;}
  3222. #hld__banlist_panel {width:500px;}
  3223. #hld__keywords_panel {width:182px;}
  3224. .hld__extra-icon-box {padding: 5px 5px 5px 0;opacity: 0;transition: all ease .2s;}
  3225. .hld__extra-icon-box:hover {opacity: 1;}
  3226. .hld__extra-icon {position: relative;padding:0 2px;text-decoration:none;cursor:pointer;}
  3227. .hld__extra-icon {text-decoration:none !important;}
  3228. span.hld__remark {color:#666;font-size:0.8em;}
  3229. .hld__banned {display: inline-block;color:#ba2026;border: 1px dashed #ba2026;padding: 10px 20px;font-weight: bold;}
  3230. .hld__banned > div {font-weight: normal;}
  3231. .hld__banned-block:hover {text-decoration: underline;cursor: pointer;}
  3232. .hld__dialog{position:absolute;padding-right:35px}
  3233. .hld__dialog>div{line-height:30px}
  3234. .hld__dialog:before{position:absolute;content:' ';width:10px;height:10px;background-color:#fff6df;left:10px;transform:rotate(45deg)}
  3235. .hld__dialog-sub-top:before{top:-6px;border-top:1px solid #591804;border-left:1px solid #591804}
  3236. .hld__dialog-sub-bottom:before{bottom:-5px;border-bottom:1px solid #591804;border-right:1px solid #591804}
  3237. .hld__dialog-buttons{display:flex;justify-content:flex-end!important;margin-top:10px}
  3238. .hld__dialog-buttons>button{cursor:pointer}
  3239. .hld__dialog-user{font-size:1.5em;color:red;margin:0 5px}
  3240. .hld__dialog input[type=text]{width:100px;margin-right:15px}
  3241. .hld__dialog-mark-table td{padding-bottom:3px}
  3242. .hld__dialog-mark-table button{padding:0 6px;margin:0;height:20px;line-height:20px;width:20px;text-align:center;cursor:pointer}
  3243. .hld__mark_history {margin-top: 10px;}
  3244. .hld__mark_history .hld__mark_history-title {font-weight: bold;}
  3245. .hld__mark_history .hld__mark_history-content {max-height: 200px;overflow: hidden;overflow-y: scroll;}
  3246. .hld__mark_history .hld__mark_history-scrollarea {display: flex;flex-wrap: wrap;width:250px;}
  3247. .hld__mark_history .hld__mark_history-content span {display: inline-block;padding: 2px 5px;border-radius: 3px;margin-right: 5px;margin-top: 5px;line-height: 20px;cursor: pointer;}
  3248. .hld__ban-desc {width: 100% !important;}
  3249. .hld__mark-desc {width: 50px;resize: none;}
  3250. .hld__mark-desc:focus {width:150px;height:3em;}
  3251. .hld__tab-content {display:flex;justify-content:space-between;flex-wrap: wrap;}
  3252. .hld__table-keyword {margin-top:10px;width:200px;}
  3253. .hld__table-keyword tr td:last-child {text-align:center;}
  3254. .hld__table-keyword input[type=text] {width:48px;text-transform:uppercase;text-align:center;}
  3255. .hld__tab-header{height:40px}
  3256. .hld__tab-header>span{margin-right:10px;padding:5px;cursor:pointer}
  3257. .hld__tab-header .hld__table-active,.hld__tab-header>span:hover{color:#591804;font-weight:700;border-bottom:3px solid #591804}
  3258. .hld__tab-content{display:none}
  3259. .hld__tab-content.hld__table-active{display:flex}
  3260. .hld__marks-container>span{display: inline-block;padding:1px 5px;border-radius:3px;margin-right:5px;margin-top:5px;color:#fff;background-color:#1f72f1}
  3261. .hld__table{table-layout:fixed;border-top:1px solid #ead5bc;border-left:1px solid #ead5bc}
  3262. .hld__table-banlist-buttons{margin-top:10px}
  3263. .hld__table thead{background:#591804;border:1px solid #591804;color:#fff}
  3264. .hld__scroll-area{position:relative;height:200px;overflow:auto;border:1px solid #ead5bc}
  3265. .hld__scroll-area::-webkit-scrollbar{width:6px;height:6px}
  3266. .hld__scroll-area::-webkit-scrollbar-thumb{border-radius:10px;box-shadow:inset 0 0 5px rgba(0,0,0,.2);background:#591804}
  3267. .hld__scroll-area::-webkit-scrollbar-track{box-shadow:inset 0 0 5px rgba(0,0,0,.2);border-radius:10px;background:#ededed}
  3268. .hld__table td,.hld__table th{padding:3px 5px;border-bottom:1px solid #ead5bc;border-right:1px solid #ead5bc;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  3269. .hld__us-action{display: inline-block;width:18px;height:18px;margin:0 3px;}
  3270. .hld__us-action svg{width:100%;height:100%;}
  3271. .hld__us-action:hover{opacity:.8}
  3272. `
  3273. }
  3274. /**
  3275. * 护眼模式
  3276. * @name EyeCareMode
  3277. * @description 此模块提供了护眼模式(绿色)
  3278. */
  3279. const EyeCareMode = {
  3280. name: 'EyeCareMode',
  3281. title: '护眼模式',
  3282. setting: {
  3283. type: 'normal',
  3284. key: 'eyeCareMode',
  3285. default: false,
  3286. title: '护眼模式',
  3287. desc: 'NGA自带的界面色调会与此功能有一定冲突\n使用前请先将NGA的界面色调设置为默认',
  3288. menu: 'left'
  3289. },
  3290. mainColor: '#cce8cc', // 主背景颜色
  3291. textColor: '#10273f', //文字颜色
  3292. buttonColor: '#c7edcc', // 按钮颜色
  3293. borderColor: '#ffffff', // 边框颜色
  3294. initFunc() {
  3295. script.setting.normal.eyeCareMode && $('body').addClass('hld__eye-care')
  3296. },
  3297. asyncStyle() {
  3298. return `
  3299. body.hld__eye-care, .hld__eye-care #msg_block_c .menu a, #msg_block_c .pager a, .hld__eye-care .nav_link, .hld__eye-care button.hld__btn:hover {color: ${this.textColor} !important;}
  3300. .hld__eye-care body, .hld__eye-care .stdbtn, .hld__eye-care .stdbtn .innerbg, .hld__eye-care .stdbtn a, .hld__eye-care .nav_root,
  3301. .hld__eye-care .nav_link, .hld__eye-care .nav_spr, .hld__eye-care .stdbtn a:hover, .hld__eye-care .row2c1, .hld__eye-care .row1c1,
  3302. .hld__eye-care .c1, .hld__eye-care .c2, .hld__eye-care .c3, .hld__eye-care .c4, .hld__eye-care .catenew,
  3303. .hld__eye-care .cateblock, .hld__eye-care .forumbox, .hld__eye-care .quote, .hld__eye-care textarea, .hld__eye-care select, .hld__eye-care input,
  3304. .hld__eye-care #m_posts .white, .hld__eye-care #m_posts .block_txt_c0, .hld__eye-care #m_posts .block_txt_c2, .hld__eye-care .block_txt_c3, .hld__eye-care .block_txt, .hld__eye-care .hld__docker-sidebar,
  3305. .hld__eye-care .hld__docker-btns > div, .hld__eye-care .forumbox th, .hld__eye-care .contentBlock, .hld__eye-care .catenew .b2, .hld__eye-care .catenew .b3,
  3306. .hld__eye-care .catenew h2, .hld__eye-care .catenew div, .hld__eye-care .topicrow .c2 > span:first-child, .hld__eye-care .urltip.nobr,
  3307. .hld__eye-care #hld__setting_panel, .hld__eye-care .hld__list-panel, .hld__eye-care .single_ttip2 .tip_title, .hld__eye-care .single_ttip2 .div2,
  3308. .hld__eye-care .postBtnPos > div, .hld__eye-care .postBtnPos .stdbtn a, .hld__eye-care .postbtnsc td, .hld__eye-care #mc > div:not(.module_wrap):not(#mainmenu), .hld__eye-care #m_nav > div:not(.nav),
  3309. .hld__eye-care {background-color: ${this.mainColor} !important;}
  3310. .hld__eye-care .nav_root, .hld__eye-care .nav_link, .hld__eye-care .nav_spr, .hld__eye-care .stdbtn, .hld__eye-care .quote, .hld__eye-care textarea,
  3311. .hld__eye-care select, .hld__eye-care input, .hld__eye-care .block_txt_c2, .hld__eye-care .block_txt_c3, .hld__eye-care .hld__docker-btns>div,
  3312. .hld__eye-care .r_container, .hld__eye-care .forumbox .postrow .stat {border:1px solid ${this.borderColor} !important;}
  3313. .hld__eye-care .b .block_txt, .hld__eye-care .block_txt.block_txt_c0 {color: #1a3959 !important;padding:0 !important;}
  3314. .hld__eye-care .forumbox.postbox {border-bottom: 2px solid ${this.borderColor} !important;border: none !important;}
  3315. .hld__eye-care .r_bar {background-color: ${this.borderColor};}
  3316. .hld__eye-care .forumbox .postrow .sigline, .hld__eye-care #m_posts .block_txt_c0 {color: ${this.borderColor} !important;border-color: ${this.borderColor} !important;}
  3317. .hld__eye-care .nav_root, .hld__eye-care .invert, .hld__eye-care #mainmenu .stdbtn .half, .hld__eye-care .catenew .invert .uitxt1, .hld__eye-care .catenew .invert .uitxt3 {color:#591804;}
  3318. .hld__eye-care:not(.hld__excel-body) #mainmenu {border-bottom: 1px solid ${this.borderColor} !important;}
  3319. .hld__eye-care #m_posts, .hld__eye-care #toptopics, .hld__eye-care #topicrows, .hld__eye-care #mc > div:not(.module_wrap):not(#mainmenu),
  3320. .hld__eye-care #msg_block_c .subblock {box-shadow:none;border-color: ${this.borderColor} !important;}
  3321. .hld__eye-care .stdbtn a, .hld__eye-care .hld__advanced-setting, .hld__eye-care .hld__docker-sidebar {border-color: ${this.borderColor};}
  3322. .hld__eye-care .block_txt.block_txt_c3 {border:none !important;}
  3323. .hld__eye-care button, .hld__eye-care .hld__setting-close, .hld__eye-care .colored_text_btn, .hld__eye-care .rep.block_txt_big {border: 1px solid ${this.borderColor} !important;background: ${this.buttonColor} !important;}
  3324. .hld__eye-care #toppedtopic table, .hld__eye-care .single_ttip2 .tip_title, .hld__eye-care .collapse_btn {border-color: ${this.borderColor} !important;}
  3325. .hld__eye-care .apd {color: ${this.borderColor} !important;}
  3326. .hld__eye-care .forumbox td:not(.c0) {border-color: ${this.borderColor} !important;border-bottom: 1px solid ${this.borderColor};border-right: 1px solid ${this.borderColor};}
  3327. .hld__eye-care .c4 {border-right:none !important;}
  3328. /* Excel 适配 */
  3329. .hld__eye-care.hld__excel-body .hld__quote-box, .hld__eye-care.hld__excel-body #m_pbtnbtm td a {background: ${this.mainColor};}
  3330. .hld__eye-care.hld__excel-body #postbbtm .stdbtn, .hld__eye-care.hld__excel-body #postbbtm td a {background: #FFF !important;}
  3331. .hld__eye-care.hld__excel-body .nav_root, .hld__eye-care.hld__excel-body .nav_spr,.hld__eye-care.hld__excel-body .nav_link,
  3332. .hld__eye-care.hld__excel-body #mainmenu .stdbtn, .hld__eye-care.hld__excel-body #mainmenu .innerbg, .hld__eye-care.hld__excel-body #mainmenu .stdbtn a {background: none !important;border: none !important;}
  3333. .hld__eye-care.hld__excel-body #mainmenu .mmdefault.cell input {background-color:#ededed !important;border-color: #c9d0dc !important;}
  3334. `
  3335. }
  3336. }
  3337. /**
  3338. * 暗黑模式
  3339. * @name DarkMode
  3340. * @description 此模块提供了暗黑主题(仿Github Dark efault Theme)
  3341. */
  3342. const DarkMode = {
  3343. name: 'DarkMode',
  3344. title: '暗黑模式',
  3345. setting: {
  3346. type: 'normal',
  3347. key: 'darkMode',
  3348. default: false,
  3349. title: '暗黑模式',
  3350. desc: 'NGA自带的界面色调会与此功能有一定冲突\n使用前请先将NGA的界面色调设置为默认\n与Excel模式不兼容,请勿混用',
  3351. menu: 'left'
  3352. },
  3353. mainColor: '#0c1117', // 主背景颜色
  3354. minorColor: '#141b22', // 次要背景颜色
  3355. textColor: '#e6edf3', // 文字颜色
  3356. muteColor: '#7d8590', // 次要文字颜色
  3357. linkColor: '#2f81f7', // 主链接颜色
  3358. buttonColor: '#1f262d', // 按钮颜色
  3359. buttonHoverColor: '#2e363d', // 按钮停留颜色
  3360. borderColor: '#21262d', // 边框颜色
  3361. initFunc() {
  3362. script.setting.normal.darkMode && $('body').removeClass('hld__eye-care').addClass('hld__dark-mode')
  3363. },
  3364. asyncStyle() {
  3365. return `
  3366. body.hld__dark-mode, .hld__dark-mode #msg_block_c .menu a, .hld__dark-mode #msg_block_c .pager a, .hld__dark-mode .nav_link, .hld__dark-mode button.hld__btn:hover {color: ${this.textColor} !important;}
  3367. .hld__dark-mode #m_threads a, .hld__dark-mode .forumbox h2, .hld__dark-mode .forumbox h1, .hld__dark-mode textarea, .hld__dark-mode select, .hld__dark-mode input,
  3368. .hld__dark-mode .catetitle {color:${this.textColor} !important;}
  3369. .hld__dark-mode #m_threads a:hover {background-color:${this.buttonHoverColor} !important;}
  3370. .hld__dark-mode svg, .hld__dark-mode svg path {fill:${this.textColor} !important;}
  3371. .hld__dark-mode a, .hld__dark-mode .uitxt1, .hld__dark-mode #hld__setting_panel .hld__sp-title a, .hld__dark-mode .author, .hld__dark-mode #m_post .author .block_txt, .hld__dark-mode #m_posts .urlincontent,
  3372. .hld__dark-mode .cell.rep, .hld__dark-mode .uitxt1 {color:${this.linkColor} !important;}
  3373. .hld__dark-mode #hld__setting_panel, .hld__dark-mode .hld__list-panel, .hld__dark-mode .single_ttip2 {border-color:${this.borderColor};box-shadow:0 8px 24px #010409;}
  3374. .hld__dark-mode #mainmenu .stdbtn a:hover {border-bottom:4px solid ${this.linkColor} !important;}
  3375. .hld__dark-mode #m_threads .block_txt_c0, .hld__dark-mode .block_txt_c0 .iconfont, .hld__dark-mode .nav_root {background-color:${this.buttonColor} !important;}
  3376. .hld__dark-mode .invert {color:${this.linkColor} !important;background-color:${this.minorColor} !important;}
  3377. .hld__dark-mode .hld__scroll-area::-webkit-scrollbar-thumb {background:#9f9f9f !important;}
  3378. .hld__dark-mode .innerbg, .hld__dark-mode .innerbg .mmdefault, .hld__dark-mode #usernamebg,
  3379. .hld__dark-mode .hld__table thead {background-color:${this.minorColor} !important;color:${this.textColor} !important;}
  3380. .hld__dark-mode .hld__tab-header > span.hld__table-active, .hld__dark-mode .hld__tab-header > span:hover {color:${this.linkColor} !important;border-color:${this.linkColor} !important;}
  3381. .hld__dark-mode .hld__dialog-sub-top:before, .hld__dark-mode .nav_root_c {background-color:${this.mainColor} !important;border-color:${this.borderColor} !important;}
  3382. .hld__dark-mode body, .hld__dark-mode .stdbtn, .hld__dark-mode .forumbox .topicrow .c3 > div:first-child, .hld__dark-mode .forumbox .topicrow .c4 > div:first-child,
  3383. .hld__dark-mode .nav_link, .hld__dark-mode .nav_spr, .hld__dark-mode .row2c1, .hld__dark-mode .row1c1, .hld__dark-mode .uitxt1, .hld__dark-mode .nav_link,
  3384. .hld__dark-mode .c1, .hld__dark-mode .c2, .hld__dark-mode .c3, .hld__dark-mode .c4, .hld__dark-mode .catenew, .hld__dark-mode .stdbtn a, .hld__dark-mode .block_txt,
  3385. .hld__dark-mode .cateblock, .hld__dark-mode .forumbox, .hld__dark-mode .quote, .hld__dark-mode textarea, .hld__dark-mode select, .hld__dark-mode input,
  3386. .hld__dark-mode #m_posts .white, .hld__dark-mode #m_posts .block_txt_c2, .hld__dark-mode .block_txt_c3, .hld__dark-mode .hld__docker-sidebar, .hld__dark-mode #hld__updated,
  3387. .hld__dark-mode .hld__docker-btns > div, .hld__dark-mode .forumbox th, .hld__dark-mode .contentBlock, .hld__dark-mode .catenew .b2, .hld__dark-mode .catenew .b3,
  3388. .hld__dark-mode .catenew h2, .hld__dark-mode .catenew div, .hld__dark-mode .topicrow .c2 > span:first-child, .hld__dark-mode .urltip.nobr, .hld__dark-mode #m_posts .small_colored_text_btn,
  3389. .hld__dark-mode #hld__setting_panel, .hld__dark-mode .hld__list-panel, .hld__dark-mode .single_ttip2 .tip_title, .hld__dark-mode .single_ttip2 .div2, .hld__dark-mode #startmenu .recent,
  3390. .hld__dark-mode .postBtnPos > div, .hld__dark-mode .postBtnPos .stdbtn a, .hld__dark-mode .postbtnsc td, .hld__dark-mode #mc > div:not(.module_wrap):not(#mainmenu), .hld__dark-mode #m_nav > div:not(.nav),
  3391. .hld__dark-mode {background-color: ${this.mainColor} !important;}
  3392. .hld__dark-mode .nav_root, .hld__dark-mode .nav_link, .hld__dark-mode .nav_spr, .hld__dark-mode .stdbtn, .hld__dark-mode .quote, .hld__dark-mode textarea,
  3393. .hld__dark-mode select, .hld__dark-mode input, .hld__dark-mode .block_txt_c2, .hld__dark-mode .block_txt_c3, .hld__dark-mode .hld__docker-btns>div,
  3394. .hld__dark-mode .r_container, .hld__dark-mode .forumbox .postrow .stat {border:1px solid ${this.borderColor} !important;}
  3395. .hld__dark-mode .b .block_txt, .hld__dark-mode .block_txt.block_txt_c0 {color: ${this.linkColor} !important;padding:0 !important;}
  3396. .hld__dark-mode .forumbox.postbox {border-bottom: 2px solid ${this.borderColor} !important;border: none !important;}
  3397. .hld__dark-mode .r_bar {background-color: ${this.borderColor};}
  3398. .hld__dark-mode .nav_root, .hld__dark-mode .invert, .hld__dark-mode #mainmenu .stdbtn .half, .hld__dark-mode .catenew .invert .uitxt1, .hld__dark-mode .catenew .invert .uitxt3,
  3399. .hld__dark-mode .single_ttip2 .tip_title, .hld__dark-mode #startmenu .item > a {color:${this.textColor} !important;}
  3400. .hld__dark-mode:not(.hld__excel-body) #mainmenu {border-bottom: 1px solid ${this.borderColor} !important;}
  3401. .hld__dark-mode #m_posts, .hld__dark-mode #toptopics, .hld__dark-mode #topicrows, .hld__dark-mode #mc > div:not(.module_wrap):not(#mainmenu),
  3402. .hld__dark-mode #msg_block_c .subblock, .hld__dark-mode #hld__updated {box-shadow:none;border-color: ${this.borderColor} !important;}
  3403. .hld__dark-mode .stdbtn a, .hld__dark-mode .hld__advanced-setting, .hld__dark-mode .hld__docker-sidebar {border-color: ${this.borderColor};}
  3404. .hld__dark-mode .block_txt.block_txt_c3 {border:none !important;}
  3405. .hld__dark-mode button, .hld__dark-mode .hld__setting-close, .hld__dark-mode .colored_text_btn, .hld__dark-mode .rep.block_txt_big,
  3406. .hld__dark-mode #main a {border: 1px solid ${this.borderColor} !important;background: ${this.buttonColor} !important;color:#c9d1d9 !important;box-shadow:none !important;}
  3407. .hld__dark-mode button:hover {background:${this.buttonHoverColor} !important;}
  3408. .hld__dark-mode button:active {outline:none !important;}
  3409. .hld__dark-mode #toppedtopic table, .hld__dark-mode .single_ttip2 .tip_title, .hld__dark-mode .collapse_btn {border-color: ${this.borderColor} !important;}
  3410. .hld__dark-mode .apd {color: ${this.borderColor} !important;}
  3411. .hld__dark-mode .forumbox td:not(.c0) {border-color: ${this.borderColor} !important;border-bottom: 1px solid ${this.borderColor};border-right: 1px solid ${this.borderColor};}
  3412. .hld__dark-mode .c4 {border-right:none !important;}
  3413. .hld__dark-mode #m_threads .replyer, .hld__dark-mode #m_threads .replyer > b, .hld__dark-mode .small_colored_text_btn, .hld__dark-mode .forumbox .postrow .stat,
  3414. .hld__dark-mode #m_posts .postrow .userval, .hld__dark-mode #m_nav .bbsinfo, .hld__dark-mode .catenew p {color:${this.muteColor} !important;}
  3415. `
  3416. }
  3417. }
  3418. /**
  3419. * 字体大小调整
  3420. * @name FontResize
  3421. * @description 此模块提供了调整字体大小的功能
  3422. */
  3423. const FontResize = {
  3424. name: 'FontResize',
  3425. title: '字体大小调整',
  3426. setting: {
  3427. type: 'advanced',
  3428. key: 'fontResize',
  3429. default: 12,
  3430. title: '字体大小调整',
  3431. desc: '字体大小调整,单位为像素(px),初始值是12,注意: 此值调整过大会导致页面混乱',
  3432. menu: 'left'
  3433. },
  3434. initFunc() {
  3435. const fontResizeInput = script.setting.advanced.fontResize
  3436. try {
  3437. const fontSize = parseInt(fontResizeInput)
  3438. if (fontSize && fontSize != 12) {
  3439. $('body').css('font-size', fontSize + 'px')
  3440. }
  3441. } catch {
  3442. script.printLog(`字体大小的值${script.setting.advanced.fontResize}无效,不是一个有效的数字`)
  3443. }
  3444. }
  3445. }
  3446. /**
  3447. * 扩展坞模块
  3448. * @name ExtraDocker
  3449. * @description 此模块提供了一个悬浮的扩展坞,来添加某些功能
  3450. * 目前添加的功能有:
  3451. * 返回顶部: 无跳转返回当前页面的第一页/刷新当页
  3452. * 打开菜单: 打开个人主菜单
  3453. * 收藏: 收藏主题
  3454. * 回复: 回复主题
  3455. * 跳转尾页: 跳转到当前帖子的尾页
  3456. */
  3457. const ExtraDocker = {
  3458. name: 'ExtraDocker',
  3459. title: '扩展坞',
  3460. settings: [{
  3461. shortCutCode: 84, // T
  3462. key: 'backTop',
  3463. title: '返回顶部'
  3464. }, {
  3465. shortCutCode: 66, // B
  3466. key: 'backBottom',
  3467. title: '跳转尾页'
  3468. }],
  3469. initFunc() {
  3470. const _this = this
  3471. const $dockerDom = $(`
  3472. <div class="hld__docker">
  3473. <div class="hld__docker-sidebar">
  3474. <svg t="1603961015993" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3634" width="64" height="64"><path d="M518.344359 824.050365c-7.879285 0-15.758569-2.967523-21.693614-9.004897l-281.403018-281.403018c-5.730389-5.730389-9.004897-13.609673-9.004897-21.693614s3.274508-15.963226 9.004897-21.693614l281.403018-281.403018c11.972419-11.972419 31.41481-11.972419 43.387229 0 11.972419 11.972419 11.972419 31.41481 0 43.387229L280.32857 511.948836l259.709403 259.709403c11.972419 11.972419 11.972419 31.41481 0 43.387229C534.0006 821.082842 526.223643 824.050365 518.344359 824.050365z" p-id="3635" fill="#888888"></path><path d="M787.160987 772.88618c-7.879285 0-15.758569-2.967523-21.693614-9.004897l-230.238833-230.238833c-11.972419-11.972419-11.972419-31.41481 0-43.387229l230.238833-230.238833c11.972419-11.972419 31.41481-11.972419 43.387229 0 11.972419 11.972419 11.972419 31.41481 0 43.387229L600.309383 511.948836l208.545218 208.545218c11.972419 11.972419 11.972419 31.41481 0 43.387229C802.817228 769.918657 794.937943 772.88618 787.160987 772.88618z" p-id="3636" fill="#888888"></path></svg>
  3475. </div>
  3476. <div class="hld__docker-btns">
  3477. <div data-type="TOP" id="hld__jump_top"><svg t="1603962702679" title="返回顶部" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9013" width="64" height="64"><path d="M528.73 161.5c-9.39-9.38-24.6-9.38-33.99 0L319.65 336.59a24.028 24.028 0 0 0-7.05 23.59A24.04 24.04 0 0 0 330 377.6c8.56 2.17 17.62-0.52 23.6-7.02l158.14-158.14 158.1 158.14a23.901 23.901 0 0 0 17 7.09c6.39 0 12.5-2.55 17-7.09 9.38-9.39 9.38-24.61 0-34L528.73 161.5zM63.89 607.09h102.79V869.5h48.04V607.09h102.79v-48.04H63.89v48.04z m518.69-48.05h-127.3c-15.37 0-30.75 5.85-42.49 17.59a59.846 59.846 0 0 0-17.59 42.49v190.3c0 15.37 5.89 30.75 17.59 42.49 11.74 11.74 27.12 17.59 42.49 17.59h127.3c15.37 0 30.75-5.85 42.49-17.59 11.7-11.74 17.59-27.12 17.59-42.49V619.17a59.903 59.903 0 0 0-17.53-42.55 59.912 59.912 0 0 0-42.55-17.54v-0.04z m12 250.38c0 2.31-0.6 5.59-3.5 8.54a11.785 11.785 0 0 1-8.5 3.5h-127.3c-3.2 0.02-6.26-1.26-8.5-3.54a11.785 11.785 0 0 1-3.5-8.5V619.17c0-2.31 0.6-5.59 3.5-8.54 2.24-2.27 5.31-3.53 8.5-3.5h127.3c2.27 0 5.55 0.64 8.5 3.55 2.27 2.24 3.53 5.31 3.5 8.5v190.29-0.05z m347.4-232.78a59.846 59.846 0 0 0-42.49-17.59H734.74V869.5h48.04V733.32h116.71a59.94 59.94 0 0 0 42.54-17.55 59.923 59.923 0 0 0 17.55-42.54v-54.07c0-15.37-5.85-30.74-17.59-42.49v-0.03z m-30.44 96.64c0 2.26-0.64 5.55-3.55 8.5a11.785 11.785 0 0 1-8.5 3.5H782.78v-78.15h116.71c2.27 0 5.59 0.6 8.54 3.5 2.27 2.24 3.53 5.31 3.5 8.5v54.15z m0 0" p-id="9014" fill="#591804"></path></svg></div>
  3478. <div data-type="MENU" id="hld__jump_menu"><svg t="1687167394269" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5137" width="48" height="48"><path d="M708.367 353.656c0-56.745-22.729-110.092-63.996-150.218s-96.132-62.224-154.494-62.224-113.229 22.099-154.498 62.224-63.996 93.473-63.996 150.218c0 43.987 13.713 86.196 39.651 122.064 7.273 10.060 21.559 12.479 31.904 5.406 10.343-7.073 12.834-20.963 5.561-31.019-20.486-28.329-31.315-61.684-31.315-96.451 0-92.585 77.471-167.911 172.694-167.911s172.689 75.325 172.689 167.911-77.471 167.906-172.694 167.906c-47.055 0-92.711 8.965-135.702 26.646-41.516 17.076-78.796 41.509-110.806 72.632-32.007 31.123-57.142 67.371-74.705 107.736-18.181 41.808-27.401 86.199-27.401 131.948 0 12.298 10.252 22.266 22.898 22.266s22.898-9.968 22.898-22.266c0-162.35 135.843-294.425 302.816-294.425 58.361 0 113.229-22.099 154.497-62.22s63.996-93.477 63.996-150.221zM530.991 631.551c0 12.298 10.252 22.266 22.898 22.266h304.337c12.647 0 22.898-9.968 22.898-22.266s-10.252-22.266-22.898-22.266h-304.337c-12.647 0-22.898 9.968-22.898 22.266zM858.229 722.671h-304.337c-12.65 0-22.898 9.968-22.898 22.266s10.252 22.266 22.898 22.266h304.337c12.647 0 22.898-9.968 22.898-22.266 0-12.294-10.252-22.266-22.898-22.266zM858.229 836.056h-304.337c-12.65 0-22.898 9.967-22.898 22.266s10.252 22.266 22.898 22.266h304.337c12.647 0 22.898-9.968 22.898-22.266 0-12.294-10.252-22.266-22.898-22.266z" fill="#591804" p-id="5138"></path></svg></div>
  3479. <div data-type="FAVOR" id="hld__jump_favor"><svg t="1687168828546" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6134" width="48" height="48"><path d="M512 776.533333l-238.933333 85.333334 8.533333-251.733334L128 405.333333l243.2-72.533333L512 128l140.8 209.066667L896 405.333333l-153.6 200.533334 8.533333 251.733333-238.933333-81.066667z m0-93.866666l149.333333 51.2-4.266666-157.866667 98.133333-123.733333-153.6-42.666667L512 277.333333 422.4 409.6l-153.6 42.666667 98.133333 123.733333-4.266666 157.866667L512 682.666667z" fill="#591804" p-id="6135"></path></svg></div>
  3480. <div data-type="REPLY" id="hld__jump_reply"><svg t="1687169791224" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8570" width="48" height="48"><path d="M415.937331 320 415.937331 96 20.001331 438.176C-6.718669 461.28-6.622669 498.784 20.033331 521.824L415.937331 864 415.937331 640C639.937331 640 847.937331 688 1023.937331 928 943.937331 480 607.937331 320 415.937331 320" p-id="8571" fill="#591804"></path></svg></div>
  3481. <div data-type="BOTTOM" id="hld__jump_bottom"><svg t="1603962680160" title="跳转至最后一页" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7501" width="64" height="64"><path d="M792.855 465.806c-6.24-6.208-14.369-9.312-22.56-9.312s-16.447 3.169-22.688 9.44l-207.91 207.74v-565.28c0-17.697-14.336-32-32-32s-32.002 14.303-32.002 32v563.712l-206.24-206.164c-6.271-6.209-14.432-9.344-22.624-9.344-8.224 0-16.417 3.135-22.656 9.407-12.511 12.513-12.48 32.768 0.032 45.248L483.536 770.38c3.265 3.263 7.104 5.6 11.136 7.135 4 1.793 8.352 2.88 13.024 2.88 1.12 0 2.08-0.544 3.2-0.64 8.288 0.064 16.608-3.009 22.976-9.408l259.11-259.292c12.48-12.511 12.448-32.8-0.127-45.248z m99.706 409.725c0 17.665-14.303 32.001-31.999 32.001h-704c-17.665 0-32-14.334-32-31.999s14.335-32 32-32h704c17.696 0 32 14.334 32 31.998z" p-id="7502" fill="#591804"></path></svg></div>
  3482. </div>
  3483. </div>
  3484. `)
  3485. $('body').append($dockerDom)
  3486. /**
  3487. * Bind:Click
  3488. * 按钮点击事件
  3489. */
  3490. $('body').on('click', '.hld__docker-btns>div', function (e) {
  3491. const type = $(this).data('type')
  3492. if (type == 'TOP') {
  3493. const $nav_link = $('#m_nav a.nav_link')
  3494. if ($nav_link.length > 0) {
  3495. $nav_link[$nav_link.length-1].click()
  3496. }
  3497. }
  3498. if (type == 'MENU') {
  3499. unsafeWindow.commonui.mainMenu.menuOpen()
  3500. unsafeWindow.commonui.mainMenu.menuOpenAct({
  3501. clientX: window.screen.width - 30,
  3502. clientY: 30,
  3503. pageX: window.screen.width - 30,
  3504. pageY: 30
  3505. }, null, 8)
  3506. }
  3507. if (type == 'FAVOR') {
  3508. const tid = script.getModule('AuthorMark').getQueryString('tid')
  3509. if (script.isForms() && tid) {
  3510. unsafeWindow.commonui.favor(e, null, tid)
  3511. }
  3512. }
  3513. if (type == 'REPLY') {
  3514. if (script.isForms()) {
  3515. window.location.href = $('#postbbtm a.rep.uitxt1').attr('href')
  3516. }
  3517. }
  3518. if (type == 'BOTTOM') {
  3519. let queryset = _this.getQuerySet()
  3520. queryset.page = 9999
  3521. let search = ''
  3522. for (let key in queryset) {
  3523. search += `${search == '' ? '?' : '&'}${key}=${queryset[key]}`
  3524. }
  3525. window.location.href = `${window.location.origin}${window.location.pathname}${search}`
  3526. }
  3527. })
  3528. },
  3529. renderAlwaysFunc(script) {
  3530. (script.isThreads() || script.isForms()) ? $('.hld__docker').show() : $('.hld__docker').hide()
  3531. $('#hld__jump_favor').toggle(script.isForms())
  3532. $('#hld__jump_reply').toggle(script.isForms())
  3533. },
  3534. shortcutFunc: {
  3535. backTop() {
  3536. $('#hld__jump_top').click()
  3537. script.popNotification('返回顶部')
  3538. },
  3539. backBottom() {
  3540. $('#hld__jump_bottom').click()
  3541. script.popNotification('最后一页')
  3542. }
  3543. },
  3544. /**
  3545. * 获取URL参数对象
  3546. * @method getQuerySet
  3547. * @return {Object} 参数对象
  3548. */
  3549. getQuerySet() {
  3550. let queryList = {}
  3551. let url = decodeURI(window.location.search.replace(/&amp;/g, "&"))
  3552. url.startsWith('?') && (url = url.substring(1))
  3553. url.split('&').forEach(item => {
  3554. let t = item.split('=')
  3555. if (t[0] && t[1]) {
  3556. queryList[t[0]] = t[1]
  3557. }
  3558. })
  3559. return queryList
  3560. },
  3561. style: `
  3562. .hld__docker{position:fixed;height:80px;width:30px;bottom:180px;right:0;transition:all ease .2s}
  3563. .hld__docker:hover{width:150px;height:300px;bottom:75px}
  3564. .hld__docker-sidebar{background:#0f0;position:fixed;height:50px;width:20px;bottom:195px;right:0;display:flex;justify-content:center;align-items:center;background:#fff6df;border:1px solid #591804;box-shadow:0 0 1px #333;border-right:none;border-radius:5px 0 0 5px}
  3565. .hld__excel-body .hld__docker-sidebar{background:#fff;border:1px solid #bbb}
  3566. .hld__docker-btns{position:absolute;top:0;left:50px;bottom:0;right:50px;display:flex;justify-content:center;align-items:center;flex-direction:column}
  3567. .hld__docker .hld__docker-btns>div{opacity:0;flex-shrink: 0;}
  3568. .hld__docker:hover .hld__docker-btns>div{opacity:1}
  3569. .hld__docker-btns>div{background:#fff6df;border:1px solid #591804;box-shadow:0 0 5px #444;width:50px;height:50px;border-radius:50%;margin:10px 0;cursor:pointer;display:flex;justify-content:center;align-items:center}
  3570. .hld__excel-body .hld__docker-btns>div{background:#fff;border:1px solid #bbb}
  3571. .hld__docker-btns svg{width:30px;height:30px;transition:all ease .2s}
  3572. .hld__docker-btns svg:hover{width:40px;height:40px}
  3573. .hld__excel-body .hld__docker-sidebar{background:#fff;border:1px solid #bbb}
  3574. .hld__excel-body .hld__docker-btns>div{background:#fff;border:1px solid #bbb}
  3575. `
  3576. }
  3577. /**
  3578. * 域名重定向
  3579. * @name DomainRedirect
  3580. * @description 此模块提供了将不同域名重定向到一个指定的目标域名
  3581. */
  3582. const DomainRedirect = {
  3583. name: 'DomainRedirect',
  3584. title: '域名重定向',
  3585. setting: {
  3586. type: 'advanced',
  3587. key: 'domainRedirectTarget',
  3588. default: '',
  3589. options: [{
  3590. label: '未配置',
  3591. value: ''
  3592. }, {
  3593. label: 'bbs.nga.cn',
  3594. value: 'bbs.nga.cn'
  3595. }, {
  3596. label: 'ngabbs.com',
  3597. value: 'ngabbs.com'
  3598. }, {
  3599. label: 'nga.178.com',
  3600. value: 'nga.178.com'
  3601. }],
  3602. title: '域名重定向目标',
  3603. desc: '此配置设置将域名重定向到的目标域名\n警告:不同域名的配置文件中的此配置应该保持一致,如不一致将会反复重定向陷入死循环!',
  3604. menu: 'left'
  3605. },
  3606. initFunc() {
  3607. const domainRedirectTarget = script.setting.advanced.domainRedirectTarget
  3608. if (domainRedirectTarget && window.location.host != domainRedirectTarget) {
  3609. const newRedirectUrl = window.location.href.replace(window.location.host, domainRedirectTarget)
  3610. window.location.replace(newRedirectUrl)
  3611. }
  3612. }
  3613. }
  3614. /**
  3615. * 用户增强
  3616. * @name UserEnhance
  3617. * @description 此模块提供了用户功能类的增强,如显示注册(不可用)天数,IP所属地等
  3618. */
  3619. const UserEnhance = {
  3620. name: 'UserEnhance',
  3621. title: '用户增强',
  3622. settings: [{
  3623. type: 'normal',
  3624. key: 'userEnhance',
  3625. default: true,
  3626. title: '用户增强',
  3627. menu: 'right'
  3628. }, {
  3629. type: 'advanced',
  3630. key: 'locationFlagMode',
  3631. default: 'FLAG_AND_TEXT',
  3632. options: [{
  3633. label: '国旗',
  3634. value: 'FLAG'
  3635. }, {
  3636. label: '文字',
  3637. value: 'TEXT'
  3638. }, {
  3639. label: '国旗加文字',
  3640. value: 'FLAG_AND_TEXT'
  3641. }],
  3642. title: '属地显示模式',
  3643. desc: '调整属地显示模式: \n全部国旗: 显示国旗不显示文字\n全部文字: 显示文字不显示国旗\n国旗加文字: 前面显示国旗后面显示文字',
  3644. menu: 'right'
  3645. }],
  3646. forumData: {
  3647. '108': '垃圾处理'
  3648. },
  3649. chart: null,
  3650. activeCount: [],
  3651. requestTasks: [],
  3652. currentUserInfo: {},
  3653. pageInfo: {},
  3654. queryTimer: null,
  3655. store: null,
  3656. initFunc() {
  3657. // 创建storage示例
  3658. this.store = script.createStorageInstance('NGA_BBS_Script__UserInfoCache')
  3659. this.preprocessing()
  3660. },
  3661. renderFormsFunc($el) {
  3662. if (!script.setting.normal.userEnhance) return
  3663. const uid = parseInt($el.find('a[name="uid"]').text())
  3664. const userInfo = unsafeWindow.commonui.userInfo.users[uid]
  3665. if (!userInfo || uid <= 0) return
  3666. const regSeconds = Math.ceil(new Date().getTime() / 1000) - userInfo.regdate
  3667. const regDays = Math.round(regSeconds / 3600 / 24)
  3668. const regYear = (regSeconds / 3600 / 24 / 365).toFixed(1)
  3669. // 插入UI
  3670. const $userEnhanceContainer = $(`<div class="hld__user-enhance hld__user-enhance-${uid}"></div>`)
  3671. const $node = $el.find('.posterinfo div.stat .clickextend').siblings('div:first-child')
  3672. $node.after($userEnhanceContainer)
  3673. $userEnhanceContainer.append(`<div><span title="注册(不可用)天数: ${regDays}天\n注册(不可用)年数: ${regYear}年">坛龄: <span class="numeric userval" name="regday">${regDays}天</span></span></div>`)
  3674. $userEnhanceContainer.append(`<div><span title="发帖数量: ${userInfo.postnum}">发帖: <span class="numeric userval" name="regday">${userInfo.postnum}</span></span></div>`)
  3675. $userEnhanceContainer.append(`<div><span style="display: inline-flex;align-items: center;" class="hld__user-location">属地: <span class="userval numeric hld__req-retry" style="margin-left:5px;">点击获取</span></span></div>`)
  3676. $userEnhanceContainer.append(`<div class="hld__qbc"><button>查看用户活动记录</button></div>`)
  3677. $userEnhanceContainer.find('.hld__user-location > span').click(e => {
  3678. if (!$(e.target).hasClass('hld__req-retry')) return
  3679. this.getUserLocation(uid)
  3680. })
  3681. $el.find('.hld__qbc > button').click(() => this.queryUserActivityRecords(userInfo))
  3682. // this.getUserLocation(uid)
  3683. },
  3684. /**
  3685. * 预处理
  3686. */
  3687. preprocessing() {
  3688. // 非实时性的任务使用异步处理
  3689. new Promise(async (resolve, reject) => {
  3690. // 初始化的时候清理超过一定时间的数据,避免无限增长数据
  3691. // 出于性能考虑,每日只执行一次
  3692. const currentDate = new Date()
  3693. const lastClear = await this.store.getItem('USERENHANCE_CLEAR_DAY')
  3694. if (lastClear != currentDate.getDate()) {
  3695. const exprieSeconds = 7 * 24 * 3600 // 7天
  3696. const currentTime = Math.ceil(currentDate.getTime() / 1000)
  3697. let removedCount = 0
  3698. this.store.iterate((value, key, iterationNumber) => {
  3699. if (key.startsWith('USERINFO_')) {
  3700. if (!value._queryTime || currentTime - value._queryTime >= exprieSeconds) {
  3701. this.store.removeItem(key)
  3702. removedCount += 1
  3703. }
  3704. }
  3705. })
  3706. .then(() => {
  3707. this.store.setItem('USERENHANCE_CLEAR_DAY', currentDate.getDate())
  3708. script.printLog(`用户增强: 已清除${removedCount}条用户超期数据`)
  3709. })
  3710. .catch(err => {
  3711. console.error('用户增强清除超期数据失败,错误原因:', err)
  3712. })
  3713. }
  3714. // 获取所有版面字典,扁平化提纯fid:name
  3715. if (!window?.script_muti_get_var_store?.data) {
  3716. await $.ajax({
  3717. url: unsafeWindow.__API.indexForumList(),
  3718. dataType: 'script',
  3719. cache: true
  3720. })
  3721. }
  3722. const _forumData = script_muti_get_var_store.data?.['0']?.all
  3723. if (_forumData && typeof _forumData == 'object') {
  3724. for (const v1 of Object.values(_forumData)) {
  3725. if (v1.content && typeof v1.content == 'object') {
  3726. for (const v2 of Object.values(v1.content)) {
  3727. if (v2.content && typeof v2.content == 'object') {
  3728. for (const v3 of Object.values(v2.content)) {
  3729. this.forumData[v3.fid] = v3.name
  3730. }
  3731. }
  3732. }
  3733. }
  3734. }
  3735. }
  3736. })
  3737. },
  3738. getUserLocation(uid) {
  3739. $('.hld__user-enhance-'+uid).find('.hld__user-location > span').attr('class', 'userval numeric loading').empty()
  3740. // 调用数据接口获取属地
  3741. this.getRemoteUserInfo(uid)
  3742. .then(remoteUserInfo => {
  3743. $('.hld__user-enhance-'+uid).find('.hld__user-location').attr('title', `IP属地: ${remoteUserInfo.ipLoc}`)
  3744. $('.hld__user-enhance-'+uid).find('.hld__user-location > span').replaceWith(this.getCountryFlag(remoteUserInfo.ipLoc))
  3745. })
  3746. .catch(err => {
  3747. $('.hld__user-enhance-'+uid).find('.hld__user-location > span').attr('class', 'userval numeric hld__req-retry').html(`获取失败(${err.status}), 点击重试`)
  3748. })
  3749. },
  3750. /**
  3751. * 调用接口获取用户信息
  3752. * @param {String} uid 用户UID
  3753. * @returns Promise 用户信息对象
  3754. */
  3755. getRemoteUserInfo(uid) {
  3756. const storageKey = `USERINFO_${uid}`
  3757. return new Promise((resolve, reject) => {
  3758. this.store.getItem(storageKey)
  3759. .then(value => {
  3760. if (value) {
  3761. resolve(value)
  3762. } else {
  3763. $.ajax({url: `https://${window.location.host}/nuke.php?__output=11&__act=get&__lib=ucp&uid=${uid}`})
  3764. .then(res => {
  3765. if (res.data && Array.isArray(res.data) && res.data.length > 0) {
  3766. const remoteUserInfo = res.data[0]
  3767. remoteUserInfo['_queryTime'] = res.time
  3768. this.store.setItem(storageKey, remoteUserInfo)
  3769. resolve(res.data[0])
  3770. }
  3771. })
  3772. .catch(err => reject(err))
  3773. }
  3774. })
  3775. })
  3776. },
  3777. /**
  3778. * 获取属地标识代码
  3779. * @param {String} chsName 中文国家名称
  3780. * @returns HTML代码
  3781. */
  3782. getCountryFlag(chsName) {
  3783. let textElement = `<span class="numeric userval" name="location">${chsName}</span>`
  3784. let flagElement = ''
  3785. if (script.setting.advanced.locationFlagMode != 'TEXT') {
  3786. const flagUrl = `https://www.huuua.com/zi/scss/icons/flag-icon-css/flags`
  3787. if (CHINESE_CONVERT_ISO3166_1[chsName]) {
  3788. flagElement = `<img class="hld__country-flag" onerror="this.style.width='auto'" alt="${chsName}" src="${flagUrl}/${CHINESE_CONVERT_ISO3166_1[chsName].toLowerCase()}.svg"/>`
  3789. } else if (CHINA_PROVINCE.includes(chsName)) {
  3790. flagElement = `<img class="hld__country-flag" onerror="this.style.width='auto'" alt="中国" src="${flagUrl}/cn.svg"/> `
  3791. const specialArea = ['香港', '澳门', '台湾'].find(name => chsName.endsWith(name))
  3792. if (specialArea) {
  3793. flagElement += `<img class="hld__country-flag" onerror="this.style.width='auto'" alt="中国${chsName}" src="${flagUrl}/${CHINESE_CONVERT_ISO3166_1['中国'+chsName].toLowerCase()}.svg"/> `
  3794. }
  3795. }
  3796. }
  3797. switch (script.setting.advanced.locationFlagMode) {
  3798. case 'FLAG':
  3799. return flagElement
  3800. case 'TEXT':
  3801. return textElement
  3802. case 'FLAG_AND_TEXT':
  3803. return flagElement + textElement
  3804. default:
  3805. return textElement
  3806. }
  3807. },
  3808. /**
  3809. * 查询用户最近活动记录(3页)
  3810. * @param {Object} userInfo 用户信息对象
  3811. */
  3812. async queryUserActivityRecords(userInfo) {
  3813. $('#hld__chart_cover').remove()
  3814. if (typeof echarts === 'undefined') {
  3815. script.popMsg('该功能所需资源库正在加载,请稍后再试', 'warn')
  3816. return
  3817. }
  3818. $('body').append(`
  3819. <div id="hld__chart_cover" class="animated zoomIn">
  3820. <a href="javascript:void(0)" class="hld__setting-close">×</a>
  3821. <div id="hld__chart_container">
  3822. <div class="loading"></div>
  3823. </div>
  3824. <div class="hld__chart-statistics">
  3825. <div class="hld__statistics-status">
  3826. <div class="hld__st-t">🏷️ 当前统计的数据量</div>
  3827. <div class="hld__st-s1">用户发布的主题(页):</div>
  3828. <div class="hld__st-s1-1">- 已统计
  3829. <span class="hld__st-c" id="hld__statistics_post_pages">0</span>页
  3830. <span class="hld__st-l" id="hld__statistics_post_status"></span>
  3831. </div>
  3832. <div class="hld__st-s1">用户回复的主题(页)</div>
  3833. <div class="hld__st-s1-1">- 已统计
  3834. <span class="hld__st-c" id="hld__statistics_reply_pages">0</span>页
  3835. <span class="hld__st-l" id="hld__statistics_reply_status"></span>
  3836. </div>
  3837. <div class="hld__st-s1">数据天数跨度</div>
  3838. <div class="hld__st-s1-1">- 已统计
  3839. <span class="hld__st-c" id="hld__statistics_days_range">-</span>天内
  3840. </div>
  3841. <div class="hld__st-t">🏷️ 统计结果</div>
  3842. <div class="hld__st-s2">✔️ 发布主题: <span class="hld__st-c" id="hld__statistics_post_count">-</span></div>
  3843. <div class="hld__st-s2">✔️ 回复主题: <span class="hld__st-c" id="hld__statistics_reply_count">-</span></div>
  3844. <div class="hld__st-s2">✔️ 总计发帖: <span class="hld__st-c" id="hld__statistics_total_count">-</span></div>
  3845. </div>
  3846. <button id="hld__chart_deep_query">深度统计</button>
  3847. </div>
  3848. </div>
  3849. `)
  3850. $('#hld__chart_cover .hld__setting-close').click(() => {
  3851. this.queryUserDeepRecords('end')
  3852. $('#hld__chart_cover').remove()
  3853. })
  3854. $('#hld__chart_cover #hld__chart_deep_query').click(() => this.queryUserDeepRecords())
  3855.  
  3856. this.activeCount = []
  3857. this.requestTasks = []
  3858. this.currentUserInfo = userInfo
  3859. this.pageInfo = {
  3860. post: {
  3861. label: '发布主题',
  3862. pages: 0,
  3863. status: '',
  3864. earliestPostdate: new Date().getTime() / 1000
  3865. },
  3866. reply: {
  3867. label: '回复主题',
  3868. pages: 0,
  3869. status: '',
  3870. earliestPostdate: new Date().getTime() / 1000
  3871. }
  3872. }
  3873. // 查询发帖记录
  3874. // tips: 由于NGA限流, 此处暂先拉取一页回复记录
  3875. for (let i=0;i<1;i++) {
  3876. // 查询发帖记录
  3877. // this.requestTasks.push(this.requestUserRecords(userInfo.uid, 'post', i+1))
  3878. // 查询回复记录
  3879. this.requestTasks.push(this.requestUserRecords(userInfo.uid, 'reply', i+1))
  3880. }
  3881. Promise.allSettled(this.requestTasks)
  3882. .then(() => {
  3883. // 渲染chart
  3884. const chartContainer = document.getElementById('hld__chart_container')
  3885. if (!chartContainer) return
  3886. this.chart = echarts.init(chartContainer)
  3887. this.statisticsCount()
  3888. this.updateChart()
  3889. })
  3890. .catch(err => {
  3891. script.popMsg(`查询【${this.pageInfo[err.type].label}第${err.page}页】数据接口失败! 原因: ${err.errMsg}`, 'err')
  3892. })
  3893. },
  3894. /**
  3895. * 查询当前用户深度活动记录(到上限)
  3896. */
  3897. async queryUserDeepRecords(status) {
  3898. if (status != 'end' && !$('#hld__chart_deep_query').hasClass('hld__query-loading')) {
  3899. // 步进统计
  3900. $('#hld__chart_deep_query').addClass('hld__query-loading').text('暂停统计')
  3901. this.queryTimer = setInterval(async () => {
  3902. try {
  3903. if (!this.pageInfo.post.status.endsWith('max')) {
  3904. await this.requestUserRecords(this.currentUserInfo.uid, 'post', this.pageInfo.post.pages + 1)
  3905. } else if (!this.pageInfo.reply.status.endsWith('max')) {
  3906. await this.requestUserRecords(this.currentUserInfo.uid, 'reply', this.pageInfo.reply.pages + 1)
  3907. }
  3908. if (this.pageInfo.post.status.endsWith('max') && this.pageInfo.reply.status.endsWith('max')) {
  3909. this.queryUserDeepRecords('end') // 停止(完成)统计
  3910. }
  3911. } catch (err) {
  3912. script.popMsg(`查询【${this.pageInfo[err.type].label}第${err.page}页】数据接口失败! 原因: ${err.errMsg}`, 'err')
  3913. this.queryUserDeepRecords('pause') // 停止(暂停)统计
  3914. } finally {
  3915. this.statisticsCount()
  3916. this.updateChart()
  3917. }
  3918. }, 2000)
  3919. } else {
  3920. // 暂停&完成统计
  3921. $('#hld__chart_deep_query').removeClass('hld__query-loading').text('继续统计')
  3922. if (status == 'end') {
  3923. $('#hld__chart_deep_query').attr('disabled', 'disabled').text('统计完成')
  3924. }
  3925. if (this.queryTimer) {
  3926. clearInterval(this.queryTimer)
  3927. this.queryTimer = null
  3928. }
  3929. }
  3930. },
  3931. /**
  3932. * 统计数量
  3933. */
  3934. statisticsCount(validList, incrField) {
  3935. if (validList && incrField) {
  3936. validList.forEach(item => {
  3937. const pName = item.parent && item.parent['2'] ? item.parent['2'] : ''
  3938. let existRecord = this.activeCount.find(p => p.fid == item.fid)
  3939. if (!existRecord) {
  3940. existRecord = {fid: item.fid, name: pName, postdate: item.postdate, value: 0, post: 0, reply: 0}
  3941. this.activeCount.push(existRecord)
  3942. }
  3943. existRecord['fid'] = item.fid
  3944. existRecord['name'] ||= pName
  3945. existRecord['value'] += 1
  3946. existRecord[incrField] += 1
  3947. })
  3948. }
  3949. const postCount = this.activeCount.reduce((p, c) => p + c.post, 0)
  3950. const replyCount = this.activeCount.reduce((p, c) => p + c.reply, 0)
  3951. // 计算统计数据
  3952. $('#hld__statistics_post_pages').text(this.pageInfo.post.pages)
  3953. $('#hld__statistics_post_status').attr('class', `hld__st-l ${this.pageInfo.post.status}`)
  3954. $('#hld__statistics_reply_pages').text(this.pageInfo.reply.pages)
  3955. $('#hld__statistics_reply_status').attr('class', `hld__st-l ${this.pageInfo.reply.status}`)
  3956. $('#hld__statistics_post_count').text(postCount)
  3957. $('#hld__statistics_reply_count').text(replyCount)
  3958. $('#hld__statistics_total_count').text(postCount + replyCount)
  3959. // 计算时间跨度
  3960. const minPostDate = Math.min(this.pageInfo.post.earliestPostdate, this.pageInfo.reply.earliestPostdate)
  3961. const daysRange = Math.ceil((new Date().getTime() / 1000 - minPostDate) / 86400)
  3962. $('#hld__statistics_days_range').text(daysRange)
  3963. },
  3964. /**
  3965. * 发起查询用户记录
  3966. */
  3967. requestUserRecords(uid, type, page) {
  3968. return new Promise((resolve, reject) => {
  3969. let url = `https://${window.location.host}/thread.php?__output=11&authorid=${uid}&page=${page}`
  3970. if (type == 'reply') {
  3971. url += '&searchpost=1'
  3972. }
  3973. $.ajax({url})
  3974. .then(postRes => {
  3975. const err = postRes.error
  3976. if (postRes.data && postRes.data.__T) {
  3977. if (page > this.pageInfo[type].pages) {
  3978. this.pageInfo[type].pages = page
  3979. }
  3980. if (this.pageInfo[type].status != 'hld__grab-max') {
  3981. this.pageInfo[type].status = ''
  3982. }
  3983. postRes.data.__T.forEach(item => {
  3984. if (item?.__P?.postdate && item.__P.postdate < this.pageInfo[type].earliestPostdate) {
  3985. this.pageInfo[type].earliestPostdate = item.__P.postdate
  3986. }
  3987. })
  3988. this.statisticsCount(postRes.data.__T, type)
  3989. }
  3990. if (err) {
  3991. const errMsg = (err && Array.isArray(err)) ? err.join(' ') : err
  3992. if (errMsg.includes('没有符合条件的结果')) {
  3993. this.pageInfo[type].status = 'hld__grab-max'
  3994. } else {
  3995. this.pageInfo[type].status = 'hld__grab-err'
  3996. reject({errMsg, type, page})
  3997. return
  3998. }
  3999. }
  4000. resolve()
  4001. })
  4002. .catch(err => reject({
  4003. errMsg: `服务器HTTP返回:${err.status}`,
  4004. type,
  4005. page
  4006. }))
  4007. })
  4008. },
  4009. /**
  4010. * 更新图表
  4011. */
  4012. updateChart() {
  4013. // 处理未命名板块
  4014. this.activeCount.forEach(item => item.name ||= (this.forumData[item.fid] || `板块FID: ${item.fid}`))
  4015. this.chart.setOption({
  4016. title: {
  4017. text: '用户活跃板块记录',
  4018. subtext: this.currentUserInfo.username || `UID: ${this.currentUserInfo.username}`,
  4019. top: 10,
  4020. left: 'center'
  4021. },
  4022. tooltip: {
  4023. formatter: function(row) {
  4024. return `${row.data.name}<br />总计: ${row.data.value}<br>发布: ${row.data.post}<br>回复: ${row.data.reply}`
  4025. }
  4026. },
  4027. toolbox: {
  4028. show: true,
  4029. bottom: 10,
  4030. left: 10,
  4031. itemSize: 16,
  4032. feature: {
  4033. saveAsImage: {show: true},
  4034. },
  4035. },
  4036. legend: {
  4037. type: 'scroll',
  4038. orient: 'vertical',
  4039. left: 10,
  4040. top: 'middle'
  4041. },
  4042. series: [{
  4043. name: '板块',
  4044. type: 'pie',
  4045. radius: '50%',
  4046. label: {
  4047. formatter: function(row) {
  4048. return `{name|${row.data.name}}\n{detail|发布: ${row.data.post}} {detail|回复: ${row.data.reply}}`
  4049. },
  4050. minMargin: 5,
  4051. edgeDistance: 10,
  4052. lineHeight: 15,
  4053. rich: {detail: {
  4054. fontSize: 10,
  4055. color: '#999'
  4056. }}
  4057. },
  4058. labelLine: {
  4059. length: 15,
  4060. length2: 0,
  4061. maxSurfaceAngle: 80
  4062. },
  4063. labelLayout: params => {
  4064. const isLeft = params.labelRect.x < this.chart.getWidth() / 2
  4065. const points = params.labelLinePoints
  4066. if (points) {
  4067. points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width
  4068. }
  4069. return {labelLinePoints: points}
  4070. },
  4071. data: this.activeCount,
  4072. emphasis: {
  4073. itemStyle: {shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)'}
  4074. }
  4075. }],
  4076. graphic: [{
  4077. type: 'image',
  4078. right: 10,
  4079. bottom: 30,
  4080. style: {
  4081. image: POWER_BY_NGASCRIPT,
  4082. width: 150
  4083. }
  4084. }]
  4085. })
  4086. $('.hld__chart-statistics').show()
  4087. },
  4088. style: `
  4089. .hld__user-enhance {display:flex;flex-wrap:wrap;}
  4090. .hld__user-enhance > div {box-sizing:border-box;width:50%;padding-right:3px;}
  4091. .hld__user-enhance span[name=location] {margin-left:5px;}
  4092. .hld__country-flag {width:20px;height:auto;margin-left:5px;}
  4093. .hld__user-location .loading {width:8px;height:8px;border:1px solid #9c958b;border-top-color:transparent;border-radius:100%;animation:loading-circle infinite 0.75s linear;}
  4094. .hld__user-location .hld__req-retry:hover {text-decoration: underline;cursor: pointer;}
  4095. .hld__qbc {width:100% !important;padding:5px 0;}
  4096. .hld__qbc > button {margin:0;}
  4097. #hld__chart_cover {position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);border-radius:10px;background:#FFF;border:1px solid #AAA;box-shadow:0 0 10px rgba(0,0,0,.3);z-index:9993;}
  4098. #hld__chart_cover > .hld__setting-close {background:#FFF;border:1px solid #AAA;color:#AAA;}
  4099. #hld__chart_cover > .hld__setting-close:hover {background:#AAA;border:1px solid #FFF;color:#FFF;}
  4100. #hld__chart_container {width:1000px;height:600px;}
  4101. #hld__chart_cover .loading {position:absolute;top: 50%;left:50%;margin-top:-20px;margin-left:-25px;width:40px;height:40px;border:2px solid #AAA;border-top-color:transparent;border-radius:100%;animation:loading-circle infinite 0.75s linear;}
  4102. .hld__chart-statistics {display:none;position:absolute;top:calc(50% - 220px);right:10px;min-width:140px;height:400px;}
  4103. .hld__statistics-status > div {padding: 2px 0;}
  4104. .hld__statistics-status > .hld__st-t {font-weight:bold;font-size:1.1em;padding-top: 25px;}
  4105. .hld__statistics-status > .hld__st-s1 {margin-top: 10px;}
  4106. .hld__statistics-status > .hld__st-s1-1 {font-size:0.9em;color:#00000073;}
  4107. .hld__statistics-status .hld__st-c {font-weight:bold;font-size:18px;color:#1677ff;margin:0px 2px;}
  4108. .hld__statistics-status .hld__st-l {display: inline-block;padding: 1px 5px;color: #FFF;transform: scale(0.8);border-radius: 5px;}
  4109. .hld__statistics-status .hld__st-l.hld__grab-max {background: #67c23a;}
  4110. .hld__statistics-status .hld__st-l.hld__grab-max:after {content: '最大';}
  4111. .hld__statistics-status .hld__st-l.hld__grab-err {background: #f56c6c;}
  4112. .hld__statistics-status .hld__st-l.hld__grab-err:after {content: '错误';}
  4113. #hld__chart_deep_query {display:flex;align-items:center;margin-top:30px;background:#1677ff;border-color:#1677ff;color:#FFF;padding:6px 15px;border-radius:8px;text-align:center;cursor:pointer;}
  4114. #hld__chart_deep_query:not(disabled):hover {opacity:.7;}
  4115. #hld__chart_deep_query:disabled {background: #67c23a;}
  4116. .hld__query-loading:before {content:"";display: inline-block;margin-right: 5px;width: 8px;height: 8px;border: 2px solid #fff;border-top-color: transparent;border-radius: 100%;animation: loading-circle infinite 0.75s linear;}
  4117. .hld__statistics-status. {padding:10px 0;}
  4118. @keyframes loading-circle {0% {transform:rotate(0);}100% {transform:rotate(360deg);}}
  4119. `
  4120. }
  4121. /**
  4122. * 插件支持模块
  4123. * @name PluginSupport
  4124. * @description 此模块提供了插件支持的能力
  4125. */
  4126. const PluginSupport = {
  4127. name: 'PluginSupport',
  4128. title: '插件支持',
  4129. pluginSetting: null,
  4130. preProcFunc() {
  4131. script.setting.plugin = {}
  4132. this.pluginSetting = script.setting.plugin
  4133. },
  4134. initFunc() {
  4135. // 添加到配置面板的设置入口
  4136. script.getModule('SettingPanel').addButton({
  4137. title: '插件管理',
  4138. desc: '插件管理',
  4139. click: () => $('#hld__plugin_panel').show()
  4140. })
  4141. try {
  4142. // 注册(不可用)插件管理面板
  4143. GM_registerMenuCommand('插件管理', () => $('#hld__plugin_panel').show())
  4144. } catch {}
  4145. // 添加插件到导入导出配置
  4146. script.getModule('BackupModule').addItem({
  4147. title: '插件配置',
  4148. writeKey: 'plugin_setting',
  4149. valueKey: 'pluginSetting',
  4150. module: this
  4151. })
  4152. // 添加插件
  4153. if (unsafeWindow.ngaScriptPlugins) {
  4154. script.printLog(`检测到个${unsafeWindow.ngaScriptPlugins.length}插件`)
  4155. unsafeWindow.ngaScriptPlugins.forEach(module => {
  4156. module.name && this.addPlugin(module)
  4157. })
  4158. }
  4159. // 加载配置
  4160. this.loadSetting()
  4161. this.initSettingPanel()
  4162. },
  4163. initSettingPanel() {
  4164. const _this = this
  4165. // 插件设置面板
  4166. const $pluginPanel = $(`
  4167. <div id="hld__plugin_panel" class="hld__list-panel animated fadeInUp">
  4168. <a href="javascript:void(0)" class="hld__setting-close" close-type="hide">×</a>
  4169. <div class="hld__plugin-header"><b>插件管理</b></div>
  4170. <div class="hld__plugin-scorllarea">
  4171. <div class="hld__plugin-content"></div>
  4172. </div>
  4173. <div class="hld__plugin-footer">
  4174. <button class="hld__btn" id="hld__plugin_getmore">获取更多插件</button>
  4175. <button class="hld__btn" id="hld__plugin_save">保存</button>
  4176. </div>
  4177. </div>
  4178. `)
  4179. const ngaScriptPlugins = unsafeWindow?.ngaScriptPlugins || []
  4180. ngaScriptPlugins.forEach(module => {
  4181. const $plugin = $(`
  4182. <div class="hld__plugin">
  4183. <div class="hld__plugin-info">
  4184. <div class="hld__plugin-name">
  4185. <a href="${module.meta.updateURL || module.meta.namespace}" target="_blank">${module.title || module.name}<span>v${module.version}</span></a>
  4186. </div>
  4187. <div class="hld__plugin-desc" title="${module.desc}">${module.desc}</div>
  4188. </div>
  4189. </div>
  4190. `)
  4191. if (module.error) {
  4192. // 插件有误
  4193. $plugin.addClass('hld__plugin-error hld__help')
  4194. $plugin.attr('error', module.error).attr('help', '插件未执行,原因: ' + module.errorMsg)
  4195. }
  4196. const pluginID = this.getPluginID(module)
  4197. if (!module.error && (module.setting || module.settings)) {
  4198. let settings = []
  4199. if (Array.isArray(module.settings)) {
  4200. settings.push(...module.settings)
  4201. } else if (Array.isArray(module.setting)) {
  4202. settings.push(...module.setting)
  4203. } else if (typeof module.setting == 'object') {
  4204. settings.push(module.setting)
  4205. }
  4206. const $pluginSettings = $('<div class="hld__plugin-settings"><table></table></div>')
  4207. // 渲染表单
  4208. settings.forEach(setting => {
  4209. let formItem = ''
  4210. const valueType = typeof setting.default
  4211. if (valueType === 'boolean') {
  4212. formItem = `<input type="checkbox" plugin-id="${pluginID}" plugin-setting-key="${setting.key}">`
  4213. }
  4214. if (valueType === 'number') {
  4215. formItem = `<input type="number" plugin-id="${pluginID}" plugin-setting-key="${setting.key}">`
  4216. }
  4217. if (valueType === 'string') {
  4218. if (setting.options) {
  4219. let t = ''
  4220. for (const option of setting.options) {
  4221. t += `<option value="${option.value}">${option.label}</option>`
  4222. }
  4223. formItem = `<select plugin-id="${pluginID}" plugin-setting-key="${setting.key}">${t}</select>`
  4224. } else if (setting.type == 'textarea') {
  4225. formItem = `<textarea rows="3" plugin-id="${pluginID}" plugin-setting-key="${setting.key}" />`
  4226. } else {
  4227. formItem = `<input type="text" plugin-id="${pluginID}" plugin-setting-key="${setting.key}" />`
  4228. }
  4229. }
  4230. $pluginSettings.find('table').append(`
  4231. <tr>
  4232. <td><span ${setting.desc ? 'class="hld__help" help="' + setting.desc + '" ' : ''}>${setting.title || setting.key}</span></td>
  4233. <td>${formItem}</td>
  4234. </tr>
  4235. `)
  4236. // 恢复设置
  4237. let defaultValue = setting.default
  4238. if (script.setting.plugin?.[pluginID]?.[setting.key] != undefined) {
  4239. defaultValue = script.setting.plugin[pluginID][setting.key]
  4240. }
  4241. const $input = $pluginSettings.find(`[plugin-id="${pluginID}"][plugin-setting-key="${setting.key}"]`)
  4242. if (typeof defaultValue == 'boolean') {
  4243. $input[0].checked = defaultValue
  4244. }
  4245. if (typeof defaultValue == 'number' || typeof defaultValue == 'string') {
  4246. $input.val(defaultValue)
  4247. }
  4248. setting.$el = $input
  4249. })
  4250. // 添加按钮及设置面板
  4251. $plugin.append($pluginSettings)
  4252. $plugin.find('.hld__plugin-info').append(`<div class="hld__plugin-expand hld__help" help="查看插件设置"><img src="${SVG_ICON_SETTING}"></div>`)
  4253. }
  4254. // 自定义按钮
  4255. if (module.buttons && Array.isArray(module.buttons) && module.buttons.length > 0) {
  4256. const $pluginButtons = $('<div class="hld__plugin-buttons"></div>')
  4257. module.buttons.forEach((button, index) => {
  4258. const buttonid = `${pluginID}_button_${index}`
  4259. const $button = $(`<button class="hld__btn" id="${buttonid}">${button.title || '未命名按钮'}</button>`)
  4260. $button.click(() => {
  4261. // 插件注入对象
  4262. const moduleProxy = this.createModuleProxy(module)
  4263. if (typeof button.action == 'string' && typeof module[button.action] == 'function') {
  4264. module[button.action].apply(moduleProxy, [button.args])
  4265. }
  4266. if (typeof button.action == 'function') {
  4267. button.action.apply(moduleProxy, [button.args])
  4268. }
  4269. })
  4270. button.$el = $button
  4271. $pluginButtons.append($button)
  4272. })
  4273. $plugin.find('.hld__plugin-settings').append($pluginButtons)
  4274. }
  4275. if ($plugin.find('.hld__plugin-settings tr').length == 0 && $plugin.find('.hld__plugin-settings button').length == 0) {
  4276. $plugin.find('.hld__plugin-settings').append('<div class="hld__plugin-nosettings">暂无可配置项</div>')
  4277. }
  4278. $pluginPanel.find('.hld__plugin-content').append($plugin)
  4279. })
  4280. if (ngaScriptPlugins.length == 0) {
  4281. $pluginPanel.find('.hld__plugin-content').html('<div class="hld_plugin-empty">未安装任何插件</div>')
  4282. }
  4283. // 展开设置
  4284. $pluginPanel.find('.hld__plugin-expand').click(function(){
  4285. $(this).parent().siblings('.hld__plugin-settings').slideToggle(100)
  4286. })
  4287. // 获取更多插件
  4288. $pluginPanel.find('#hld__plugin_getmore').click(function(){
  4289. window.open('https://gf.qytechs.cn/zh-CN/scripts?q=NGA%E4%BC%98%E5%8C%96%E6%91%B8%E9%B1%BC%E4%BD%93%E9%AA%8C%E6%8F%92%E4%BB%B6')
  4290. })
  4291. // 保存设置
  4292. $pluginPanel.find('#hld__plugin_save').click(function(){
  4293. _this.saveSetting()
  4294. })
  4295. $('body').append($pluginPanel)
  4296. },
  4297. /**
  4298. * 添加插件
  4299. * @param {*} module 插件模块
  4300. */
  4301. addPlugin(module) {
  4302. module.type = 'plugin'
  4303. module.title = module.title || module.meta.name
  4304. module.desc = module.desc || module.meta.description
  4305. module.author = module.author || module.meta.author || 'Unknown'
  4306. module.version = module.meta.version || '1.0.0'
  4307. // 检测是否与标准模块命名冲突,此检测是为了未来可以合并插件功能进主脚本内而做的预设性检查
  4308. const pluginID = this.getPluginID(module)
  4309. const existBasicModule = script.modules.find(m => m.type != 'plugin' && m.name == module.name)
  4310. if (existBasicModule) {
  4311. module.error = 'UNIQUE_BASIC'
  4312. module.errorMsg = `插件[${module.name}]与标准模块重名,可能是标准模块已有此功能`
  4313. script.printLog(`[${module.name}]${module.errorMsg}`)
  4314. return
  4315. }
  4316. // 检测重复插件,插件Name@Author字符串为唯一key值,如重复导入将不会运行
  4317. const duplicatePlugin = script.modules.find(m => m.type == 'plugin' && `${m.name}@${this.hashCode(m.author)}` == pluginID)
  4318. if (duplicatePlugin) {
  4319. module.error = 'DUPLICATED'
  4320. module.errorMsg = `重复导入,插件[${pluginID}]当前已加载`
  4321. script.printLog(`[${module.name}]${module.errorMsg}`)
  4322. return
  4323. }
  4324. // 插件预处理函数
  4325. if (module.preProcFunc) {
  4326. try {
  4327. module.preProcFunc()
  4328. } catch (error) {
  4329. this.printLog(`[${module.name}]插件在[preProcFunc()]中运行失败!`)
  4330. console.log(error)
  4331. }
  4332. }
  4333. // 添加设置
  4334. const addSetting = setting => {
  4335. // // 插件配置
  4336. // if (setting.shortCutCode && script.setting.plugin.shortcutKeys) {
  4337. // script.setting.plugin.shortcutKeys.push(setting.shortCutCode)
  4338. // }
  4339. if (setting.key) {
  4340. if (!script.setting.plugin[pluginID]) {
  4341. script.setting.plugin[pluginID] = {}
  4342. }
  4343. script.setting.plugin[pluginID][setting.key] = setting.default ?? ''
  4344. script.setting.original.push(Object.assign({type: 'plugin', pluginID}, setting))
  4345. }
  4346. }
  4347. // 功能板块
  4348. if (module.setting && !Array.isArray(module.setting)) {
  4349. addSetting(module.setting)
  4350. }
  4351. if (module.settings && Array.isArray(module.settings)) {
  4352. for (const setting of module.settings) {
  4353. addSetting(setting)
  4354. }
  4355. }
  4356. // 添加样式
  4357. if (module.style) {
  4358. script.style += module.style
  4359. }
  4360. const moduleProxy = this.createModuleProxy(module)
  4361. script.modules.push(moduleProxy)
  4362. },
  4363. /**
  4364. * 插件注入对象
  4365. * @param {*} module 插件模块
  4366. */
  4367. createModuleProxy(module) {
  4368. const pluginID = this.getPluginID(module)
  4369. return new Proxy(module, {
  4370. get: function (target, key) {
  4371. if (key == 'mainScript') return script // 主脚本
  4372. if (key == 'pluginID') return pluginID // 插件ID
  4373. if (key == 'pluginSettings') return script.setting.plugin[pluginID] // 插件保存配置
  4374. // 插件输入控件dom
  4375. if (key == 'pluginInputs') {
  4376. const pluginInputs = {}
  4377. Object.keys(script.setting.plugin[pluginID]).forEach(key => {
  4378. pluginInputs[key] = $(`[plugin-id="${pluginID}"][plugin-setting-key="${key}"]`)
  4379. })
  4380. return pluginInputs
  4381. }
  4382. return target[key]
  4383. },
  4384. set: function (target, key, newValue) {
  4385. if (['mainScript', 'pluginID', 'pluginSettings', 'pluginInputs'].includes(key)) {
  4386. throw new TypeError(`[${key}]为插件保留字段,不可手动设值`)
  4387. }
  4388. target[key] = newValue
  4389. return true
  4390. }
  4391. })
  4392. },
  4393. /**
  4394. * 读取插件配置
  4395. * @method loadSetting
  4396. */
  4397. loadSetting() {
  4398. try {
  4399. // 插件设置
  4400. const pluginSettingStr = script.getValue('hld__NGA_plugin_setting')
  4401. if (pluginSettingStr) {
  4402. let localPluginSetting = JSON.parse(pluginSettingStr)
  4403. for (const pluginName of Object.keys(localPluginSetting)) {
  4404. let currentSetting = script.setting.plugin[pluginName]
  4405. let localSetting = localPluginSetting[pluginName]
  4406. if (currentSetting) {
  4407. for (let k in currentSetting) {
  4408. !localSetting.hasOwnProperty(k) && (localSetting[k] = currentSetting[k])
  4409. }
  4410. for (let k in localSetting) {
  4411. !currentSetting.hasOwnProperty(k) && delete localSetting[k]
  4412. }
  4413. }
  4414. script.setting.plugin[pluginName] = localSetting
  4415. }
  4416. }
  4417. } catch(e) {
  4418. script.throwError(`【NGA-Script】读取插件配置文件出现错误,无法加载配置文件!\n错误问题: ${e}\n\n请尝试使用【修复脚本】来修复此问题`)
  4419. }
  4420. },
  4421. /**
  4422. * 保存插件配置
  4423. * @method saveSetting
  4424. * @param {String} msg 自定义消息信息
  4425. */
  4426. saveSetting (msg='保存插件配置成功,刷新页面生效') {
  4427. script.modules.forEach(module => {
  4428. if (module.type == 'plugin' && module.name) {
  4429. const pluginID = this.getPluginID(module)
  4430. const pluginSetting = Object.assign({}, script.setting.plugin[pluginID])
  4431. const $controls = $(`[plugin-id="${pluginID}"]`)
  4432. if (pluginSetting && $controls) {
  4433. $controls.each((index, element) => {
  4434. const k = $(element).attr('plugin-setting-key')
  4435. const inputType = $(element)[0].nodeName
  4436. const originalSetting = script.setting.original.find(s => s.type == 'plugin' && s.pluginID == pluginID && s.key == k)
  4437. const valueType = typeof originalSetting.default
  4438. if (inputType == 'SELECT') {
  4439. pluginSetting[k] = $(element).val()
  4440. } else {
  4441. if (valueType == 'boolean') {
  4442. pluginSetting[k] = $(element)[0].checked
  4443. }
  4444. if (valueType == 'number') {
  4445. pluginSetting[k] = +$(element).val()
  4446. }
  4447. if (valueType == 'string') {
  4448. pluginSetting[k] = $(element).val()
  4449. }
  4450. }
  4451. })
  4452. // 预检查配置参数
  4453. if (module.beforeSaveSettingFunc) {
  4454. const errorMsg = module.beforeSaveSettingFunc(pluginSetting)
  4455. if (errorMsg && typeof errorMsg === 'string') {
  4456. script.throwError(`插件【${module.title || module.name || 'UNKNOW'}】检查配置返回错误: \n${'-'.repeat(50)}\n${errorMsg}`)
  4457. }
  4458. }
  4459. script.setting.plugin[pluginID] = Object.assign({}, pluginSetting)
  4460. }
  4461. }
  4462. })
  4463. script.setValue('hld__NGA_plugin_setting', JSON.stringify(script.setting.plugin))
  4464. msg && script.popMsg(msg)
  4465. $('#hld__plugin_panel').hide()
  4466. },
  4467. /**
  4468. * 获取插件的唯一ID
  4469. * @param {obj} module 插件对象
  4470. */
  4471. getPluginID(module) {
  4472. return `${module.name}@${this.hashCode(module.author)}`
  4473. },
  4474. /**
  4475. * 生成字符串哈希值
  4476. * @param {String} str 字符串
  4477. * @returns 哈希值
  4478. */
  4479. hashCode(str) {
  4480. return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0)
  4481. },
  4482. style: `
  4483. #hld__plugin_panel {display:none;width:400px;min-height:300px;}
  4484. #hld__plugin_panel .hld__plugin-header {min-height:20px;}
  4485. #hld__plugin_panel .hld__plugin-scorllarea {margin:10px 0;height:300px;padding-right:10px;overflow-y:auto;border-top:1px solid #e0c19e;border-bottom:1px solid #e0c19e;}
  4486. #hld__plugin_panel .hld__plugin-content {height:auto;}
  4487. #hld__plugin_panel .hld_plugin-empty {margin-top:20%;text-align:center;font-size:16px;color:#666;}
  4488. #hld__plugin_panel .hld__plugin-footer {min-height:32px;display:flex;justify-content:space-between;}
  4489. #hld__plugin_panel button {transition:all .2s ease;cursor:pointer;}
  4490. .hld__plugin {padding:10px 0;border-bottom:1px dashed #666;}
  4491. .hld__plugin-error {text-decoration: line-through;}
  4492. .hld__plugin-info {position:relative;padding-right:30px;box-sizing:border-box;}
  4493. .hld__plugin-name {margin-bottom:5px;}
  4494. .hld__plugin-name a {font-weight:bold;font-size:16px;color:#591804;}
  4495. .hld__plugin-name span {margin-left:4px;font-size:70%;color:#666;}
  4496. .hld__plugin-desc {white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  4497. .hld__plugin-expand {width:20px;display:flex;align-items:center;cursor:pointer;position:absolute;top:10px;right:0px;}
  4498. .hld__plugin-expand img {width:100%;transition:all .2s ease}
  4499. .hld__plugin-expand:hover img {width:100%;transform:rotate(45deg);}
  4500. .hld__plugin-settings {display:none;width:100%;height:auto;border-top:1px dashed #999;margin-top:10px;padding-top:10px;}
  4501. .hld__plugin-settings table td {padding-right:10px;}
  4502. .hld__plugin-settings textarea {resize:none;}
  4503. .hld__plugin-settings input[type=number] {border: 1px solid #e6c3a8;box-shadow: 0 0 2px 0 #7c766d inset;border-radius: 0.25em;}
  4504. .hld__plugin-buttons {padding-top:5px;}
  4505. .hld__plugin-buttons > button {margin-right:5px;margin-top:5px;}
  4506. .hld__plugin-nosettings {color:#666;}
  4507. `
  4508. }
  4509.  
  4510. /**
  4511. * 初始化脚本
  4512. */
  4513. const script = new NGABBSScript()
  4514. /**
  4515. * 添加模块
  4516. */
  4517. script.addModule(SettingPanel)
  4518. script.addModule(ShortCutKeys)
  4519. script.addModule(BackupModule)
  4520. script.addModule(PluginSupport)
  4521. script.addModule(RewardPanel)
  4522. script.addModule(HideAvatar)
  4523. script.addModule(HideSmile)
  4524. script.addModule(HideImage)
  4525. script.addModule(ImgResize)
  4526. script.addModule(HideSign)
  4527. script.addModule(HideHeader)
  4528. script.addModule(ExcelMode)
  4529. script.addModule(FoldQuote)
  4530. script.addModule(UserEnhance)
  4531. script.addModule(LinkTargetBlank)
  4532. script.addModule(DirectLinkJump)
  4533. script.addModule(ImgEnhance)
  4534. script.addModule(AuthorMark)
  4535. script.addModule(AutoPage)
  4536. script.addModule(KeywordsBlock)
  4537. script.addModule(MarkAndBan)
  4538. script.addModule(EyeCareMode)
  4539. script.addModule(DarkMode)
  4540. script.addModule(FontResize)
  4541. script.addModule(ExtraDocker)
  4542. script.addModule(DomainRedirect)
  4543. /**
  4544. * 运行脚本
  4545. */
  4546. script.run()
  4547. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址