U2历史记录

查看种子历史记录

Cài đặt script này?
Script được tác giả gợi ý

Bạn có thế thích U2实时预览BBCODE

Cài đặt script này
  1. // ==UserScript==
  2. // @name U2历史记录
  3. // @namespace https://u2.dmhy.org/
  4. // @version 0.8.0
  5. // @description 查看种子历史记录
  6. // @author kysdm
  7. // @grant none
  8. // @match *://u2.dmhy.org/details.php?*
  9. // @match *://u2.dmhy.org/offers.php?*
  10. // @match *://u2.dmhy.org/forums.php?action=viewtopic*
  11. // @icon https://u2.dmhy.org/favicon.ico
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js
  13. // @require https://unpkg.com/thenby@1.3.4/thenBy.min.js
  14. // @require https://unpkg.com/diff@5.1.0/dist/diff.js
  15. // @require https://cdn.jsdelivr.net/npm/diff2html@3.4.40/bundles/js/diff2html.min.js
  16. // @license Apache-2.0
  17. // ==/UserScript==
  18.  
  19. /*
  20. 本脚本基于 Bamboo Green 界面风格进行修改
  21. /*
  22. /*
  23. GreasyFork 地址
  24. https://greasyfork.org/zh-CN/scripts/428545
  25. */
  26.  
  27. /*
  28. 使用说明
  29. https://u2.dmhy.org/forums.php?action=viewtopic&topicid=13495&page=p150133#pid150133
  30. */
  31.  
  32. /*
  33. 更新日志
  34. https://github.com/kysdm/u2_share/commits/main/u2share_history.user.js
  35. */
  36.  
  37. 'use strict';
  38.  
  39. // 声明全局变量
  40. var lang, torrent_id, db, user_id, topicid, key, token;
  41.  
  42. (async () => {
  43. // 初始化
  44. addGlobalStyles(`.diff-container{display:flex;align-items:flex-start;justify-content:flex-start;}.diff-cell{border:none;padding:0;margin-left:5px;flex:1;}.draw-div{box-sizing:border-box;max-width:100%;min-height:15px;max-height:600px;margin:5px;overflow:auto;border-top:1px solid #bfbfbf;border-bottom:1px solid #bfbfbf;}.diff-table{width:100%;border-left:1px solid #bfbfbf;border-right:1px solid #bfbfbf;background-color:white;}.diff-table table,.diff-table table td{background-color:transparent;border:none;vertical-align:top;}.diff-table,.diff-table table{border-collapse:collapse;box-sizing:border-box;table-layout:fixed;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px;}.diff-table tbody{vertical-align:top;}.diff-table del{text-decoration:none;background-color:#ff818266;}.diff-table ins{text-decoration:none;background-color:#abf2bc;}.diff-linenumber{text-align:right;vertical-align:top;width:3em;border:none;color:#6e7781;font-size:12px;}.diff-linenumber-delete{background-color:#ffd7d5;}.diff-linenumber-insert{background-color:#ccffd8;}.diff-line-text-delete{background-color:#ffebe9;}.diff-line-text-insert{background-color:#e6ffec;}.diff-linenumber-empty,.diff-text-cell-empty{background-color:#d0d8e080;}.diff-line-text{display:inline-block;white-space:pre-wrap;overflow-wrap:break-word;word-break:break-word;box-sizing:border-box;width:auto;font-size:12px;}.diff-line-prefix{background:none;word-wrap:break-word;display:inline;font-size:12px;box-sizing:border-box;vertical-align:top;}.diff-line-prefix-delete::before{content:" - ";}.diff-line-prefix-insert::before{content:" + ";}.diff-line-prefix-empty::before{content:" ";}.diff-text-cell,.diff-text-cell-empty{width:auto;white-space:pre;border-left:none;border-right:1px solid #bfbfbf;border-top:none;border-bottom:none;}`);
  45. lang = new lang_init($('#locale_selection').val()); // 获取当前网页语言
  46. let em = /.*id=(?<tid>\d{3,5})/i.exec(location.search); if (em) torrent_id = em.groups.tid; else torrent_id = null; // 当前种子ID
  47. topicid = location.href.match(/topicid=(\d+)/i) || ['', '']; if (topicid[1] !== '') topicid = topicid[1];
  48. user_id = $('#info_block').find('a:first').attr('href').match(/\.php\?id=(\d{3,5})/i) || ['', '']; if (user_id[1] !== '') user_id = user_id[1]; // 当前用户ID
  49. db = localforage.createInstance({ name: "history" });
  50. // key = await db.getItem('key');
  51. token = await db.getItem('token');
  52. if (token === null || token.length !== 96) { new auth(); return; };
  53. if (torrent_id && /\/(offers|details)\.php/i.test(location.pathname) && $('#outer').find('h2').text().match(/错误|錯誤|Ошибка|error/i)) { torrentInfoHistoryReset(); torrentCommentHistoryReset(); }// 为已经删除的种子显示历史
  54. else if (torrent_id && '/offers.php' === location.pathname && !/(cmtpage|offer_vote|vote)=(1|p)/i.test(location.href) && /off_details=1/i.test(location.href)) { torrentInfoHistory(); torrentCommentHistory(); } // 为正常种子显示历史
  55. else if (torrent_id && '/details.php' === location.pathname && !/(cmtpage|offer_vote|vote)=(1|p)/i.test(location.href)) { torrentInfoHistory(); torrentCommentHistory(); } // 为正常种子显示历史
  56. else if (torrent_id && /\/(offers|details)\.php/i.test(location.pathname) && /cmtpage=1/i.test(location.href)) { torrentCommentHistory(); } // 为正常种子显示历史 <仅评论>
  57. else if (/\/forums\.php\?action=viewtopic/i.test(location.href) && $('#outer').find('h2').text().match(/错误|錯誤|Ошибка|error/i)) { forumCommentHistoryReset(); } // 为被删除的论坛帖子显示历史
  58. else if (/\/forums\.php\?action=viewtopic/i.test(location.href)) { forumCommentHistory(); }; // 为论坛帖子显示历史
  59. })();
  60.  
  61. function auth() {
  62.  
  63. $('#outer').html(`<h1 align="center">U2种子历史记录 自动鉴权工具</h1>
  64. <table border="0" align="center" cellspacing="0" cellpadding="5">
  65. <tbody>
  66. <tr>
  67. <td valign="top" width="500" align="center"><span style="word-break: break-all; word-wrap: break-word;">
  68. <bdo dir="ltr">点击开始按钮,将自动进行鉴权,提示完成请刷新界面。<br>(建议手动备份下个人说明)<br></bdo></span></td>
  69. </tr>
  70. <tr>
  71. <td valign="top" align="left"><span style="word-break: break-all; word-wrap: break-word;">
  72. <bdo id="auth_log" dir="ltr"></bdo></span></td>
  73. </tr>
  74. <tr>
  75. <td align="center">
  76. <button id="auth_token" class="codebuttons" style="font-size:11px;margin-right:3px;" type="button">开始鉴权</button>
  77. <button id="auth_token_d" class="codebuttons" style="font-size:11px;margin-right:3px;" type="button">已有TOKEN</button>
  78. <button id="auth_token_reload" class="codebuttons" style="font-size:11px;margin-right:3px;display:none;" type="button">刷新网页</button>
  79. </td>
  80. </tr>
  81. </tbody>
  82. </table>`);
  83.  
  84. $("#auth_token_d").click(async function () {
  85. let __token = window.prompt("请输入Token"); // 弹窗提示输入Token
  86. if (__token === null || __token.length === 0) return; // 没有任何输入时 无视本次操作
  87. else if (__token.length !== 96) {
  88. await outPutLog(`Token: ${__token}`);
  89. await outPutLog(`Token长度不正确`);
  90. return;
  91. } // TOKEN长度不正确时 无视本次操作
  92. else {
  93. await db.setItem('token', __token);
  94. await outPutLog(`Token: ${__token}`);
  95. await outPutLog('鉴权结束');
  96. $("#auth_token").hide();
  97. $("#auth_token_d").hide();
  98. $("#auth_token_reload").show();
  99. };
  100. });
  101.  
  102. $("#auth_token_reload").click(function () {
  103. window.location.reload();
  104. });
  105.  
  106. function getProfile() {
  107. return new Promise(async (resolve, reject) => {
  108. $.ajax({
  109. type: 'get',
  110. url: 'https://u2.dmhy.org/usercp.php?action=personal',
  111. cache: false,
  112. success: async r => {
  113. const usercp = document.createElement('div');
  114. usercp.innerHTML = r;
  115. // const profile = $(usercp).find('[name="info"]').text(); // 获取用户信息
  116. const profile = {
  117. "action": "personal",
  118. "type": "save",
  119. "acceptpms": $(usercp).find('[name="acceptpms"]:checked').val(), // 接受以下短讯
  120. // 如果不需要开启对应功能,则不发送该参数
  121. "deletepms": $(usercp).find('[name="deletepms"]').is(':checked') ? 'on' : '', // 回复后删除短讯
  122. "savepms": $(usercp).find('[name="savepms"]').is(':checked') ? 'on' : '', // 保存短讯至发件箱
  123. "commentpm": $(usercp).find('[name="commentpm"]').is(':checked') ? 'yes' : '', // 我发布的种子有新评论时通知我
  124. "atpm": $(usercp).find('[name="atpm"]').is(':checked') ? '1' : '',// 有人在群聊区@我时通知
  125. "quotepm": $(usercp).find('[name="quotepm"]').is(':checked') ? '1' : '',// 有人在论坛、种子评论或候选评论引用我时通知。
  126. // 如果不需要开启对应功能,则不发送该参数
  127. "country": $(usercp).find('[name="country"]').val(), // 国家/地区
  128. "download": $(usercp).find('[name="download"]').val(),// 下行带宽
  129. "upload": $(usercp).find('[name="upload"]').val(),// 上行带宽
  130. "isp": $(usercp).find('[name="isp"]').val(),// 互联网服务提供商
  131. "savatar": $(usercp).find('[name="savatar"]').val(), // 选择头像
  132. "avatar": $(usercp).find('[name="avatar"]').val(), // 自定义头像
  133. "info": $(usercp).find('[name="info"]').text() // 个人说明
  134. };
  135. let profileAuth = { ...profile }; // 复制
  136. profileAuth.info = `-----BEGIN API KEY-----\n${key}\n-----END API KEY-----\n\n${profile.info}`; // 在个人说明加入鉴权信息
  137. // const p = profileAuth.info.replace(/\r\n/g, () => { return '<br>' }).replace(/\n/g, () => { return '<br>' }).replace(/\r/g, () => { return '<br>' });
  138. // await outPutLog(`请检查准备写入个人说明的BBCODE是否正确<br><br><table class="spoiler" width="100%">
  139. // <tbody>
  140. // <tr>
  141. // <td class="colhead">个人说明&nbsp;&nbsp;<button class="spoiler-button-show" style="">检查一下</button>
  142. // <button id="auth_profile_check" class="spoiler-button-hide" style="display: none;">检查完成</button></td>
  143. // </tr>
  144. // <tr>
  145. // <td><span class="spoiler-content" style="display: none;">${p}</span></td>
  146. // </tr>
  147. // </tbody>
  148. // </table>`);
  149. await db.setItem('profile', profile); // 存储用户信息
  150. // $("#auth_profile_check").click(async function (ev) {
  151. // $(this).hide();
  152. // $(this).siblings(".spoiler-button-show").show();
  153. // $(this).parentsUntil(".spoiler").find("span.spoiler-content:first").hide();
  154. // ev.preventDefault();
  155. // return resolve(profileAuth);
  156. // });
  157. return resolve(profileAuth);
  158. },
  159. error: async d => {
  160. await outPutLog('获取个人说明BBCODE失败');
  161. await outPutLog(`错误信息: ${d.responseText}`);
  162. return reject(Error(d.responseText));
  163. },
  164. });
  165. });
  166. };
  167.  
  168. function postProfile(data) {
  169. return new Promise(async (resolve, reject) => {
  170. $.ajax({
  171. type: 'post',
  172. url: 'https://u2.dmhy.org/usercp.php',
  173. cache: false,
  174. contentType: "application/x-www-form-urlencoded",
  175. data: data,
  176. success: async r => {
  177. await outPutLog('修改个人说明BBCODE成功');
  178. return resolve(key);
  179. },
  180. error: async d => {
  181. await outPutLog('修改个人说明BBCODE失败');
  182. await outPutLog(`错误信息: ${d.responseText}`);
  183. return reject(Error(d.responseText));
  184. },
  185. });
  186. });
  187. };
  188.  
  189. function getAuthKey() {
  190. return new Promise(async (resolve, reject) => {
  191. $.ajax({
  192. type: 'post',
  193. url: 'https://u2.kysdm.com/api/v1/token',
  194. contentType: "application/json",
  195. dataType: 'json',
  196. // async: false,
  197. data: JSON.stringify({ "uid": user_id }),
  198. success: async function (d) {
  199. if (d.msg === 'success') {
  200. key = d.data.key
  201. db.setItem('key', key);
  202. await outPutLog('获取Key成功');
  203. await outPutLog(`Key: ${key}`);
  204. return resolve(key);
  205. } else {
  206. await outPutLog('获取Key失败');
  207. await outPutLog(`错误信息: ${JSON.stringify(d)}`);
  208. return reject(Error('获取Key失败'));
  209. };
  210. },
  211. error: async function (d) {
  212. await outPutLog('获取Key失败');
  213. await outPutLog(`错误信息: ${d.responseText}`);
  214. return reject(Error('获取Key失败'));
  215. },
  216. });
  217. });
  218. };
  219.  
  220. function getToken() {
  221. return new Promise(async (resolve, reject) => {
  222. $.ajax({
  223. type: 'post',
  224. url: 'https://u2.kysdm.com/api/v1/token',
  225. contentType: "application/json",
  226. dataType: 'json',
  227. data: JSON.stringify({ "uid": user_id, "key": key }),
  228. success: async function (d) {
  229. if (d.msg === 'success') {
  230. let __token = d.data.token
  231. await outPutLog('获取Token成功');
  232. await outPutLog(`Token: ${__token}`);
  233. await db.setItem('token', __token);
  234. return resolve(__token);
  235. } else {
  236. await outPutLog('获取Token失败');
  237. await outPutLog(`错误信息: ${JSON.stringify(d)}`);
  238. return reject(Error('获取Token失败'));
  239. };
  240. },
  241. error: async function (d) {
  242. await outPutLog('获取Token失败');
  243. await outPutLog(`错误信息: ${d.responseText}`);
  244. return reject(Error('获取Token失败'));
  245. },
  246. });
  247. });
  248. };
  249.  
  250. function outPutLog(text) {
  251. return new Promise(async (resolve, reject) => {
  252. const log = $('#auth_log').html();
  253. $('#auth_log').html(`${log}${getDateString()} - ${text}<br>`);
  254. resolve(await sleep(0));
  255. });
  256. };
  257.  
  258. async function sleep(interval) {
  259. return new Promise(resolve => {
  260. setTimeout(resolve, interval);
  261. })
  262. };
  263.  
  264. $("#auth_token").click(async function () {
  265. $("#auth_token").attr('disabled', "true");
  266. $("#auth_token_d").attr('disabled', "true");
  267. await outPutLog('鉴权开始');
  268. await outPutLog('获取鉴权所需的Key');
  269. getAuthKey()
  270. .then(async () => {
  271. await outPutLog('获取个人说明BBCODE');
  272. return getProfile();
  273. })
  274. .then(async data => {
  275. await outPutLog('修改个人说明BBCODE');
  276. await postProfile(data);
  277. })
  278. .then(async () => {
  279. await outPutLog('获取鉴权所需的Token');
  280. await getToken();
  281. })
  282. .then(async () => {
  283. await outPutLog('还原个人说明BBCODE');
  284. await postProfile(await db.getItem('profile'));
  285. })
  286. .catch(async err => {
  287. await outPutLog(err);
  288. })
  289. .finally(async () => {
  290. await outPutLog('鉴权结束');
  291. $("#auth_token").hide();
  292. $("#auth_token_d").hide();
  293. $("#auth_token_reload").show();
  294. });
  295. });
  296. };
  297.  
  298. function forumCommentHistoryReset() {
  299. const errorstr = $('#outer').find('td.text').text();
  300. // 正在努力加载中...
  301. $('#outer').find('td.text').html(errorstr + '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>' + lang['history_text_loading'] + '</i>');
  302.  
  303. $.ajax({
  304. type: 'post',
  305. url: 'https://u2.kysdm.com/api/v1/comment',
  306. contentType: "application/json",
  307. dataType: 'json',
  308. data: JSON.stringify({ "uid": user_id, "token": token, "topicid": topicid, "type": "forum" }),
  309. success: function (d) {
  310. if (d.msg === 'success') {
  311. console.log('获取论坛评论成功');
  312. let __comment = d.data.comment[topicid].sort(firstBy((a, b) => a.pid - b.pid).thenBy((a, b) => b.self - a.self)); // 如果用self排序,消息顺序不正确,则改用编辑日期排序
  313.  
  314. if (__comment.length === 0) { // 没有评论 可以说不会出现这种情况
  315. console.log('没有历史记录.');
  316. $('#outer').find('td.text').html(errorstr + '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + lang['history_text_empty'] + '</i>');
  317. return;
  318. };
  319.  
  320. const locked = __comment.some(x => x.locked === 1); // 检查帖子是否被锁定
  321.  
  322. // 计算pid出现次数
  323. let pidList = __comment.map(x => x.pid);
  324. let counts = new Object();
  325. pidList.forEach(x => counts[x] = counts[x] ? counts[x] + 1 : 1);
  326. const pidListSet = [...new Set(pidList)]; // 去重
  327.  
  328. // 还原网页
  329. $('#outer').html(`
  330. <table class="main" width="940" border="0" cellspacing="0" cellpadding="0">
  331. <tbody>
  332. <tr>
  333. <td class="embedded" align="center">
  334. <h1 align="center"><span id="top"></span></h1><br><br>
  335. </td>
  336. </tr>
  337. </tbody>
  338. </table>
  339. <table class="main" width="940" border="0" cellspacing="0" cellpadding="0">
  340. <tbody>
  341. <tr>
  342. <td class="embedded">
  343. <table width="100%" border="1" cellspacing="0" cellpadding="10">
  344. <tbody>
  345. <tr>
  346. <td id="comments" class="text">
  347. </td>
  348. </tr>
  349. </tbody>
  350. </table>
  351. </td>
  352. </tr>
  353. </tbody>
  354. </table>`
  355. );
  356.  
  357. // 还原标题
  358. $('span[id="top"]').html(`${__comment[0]['topics']}${locked ? '&nbsp;&nbsp;<b>[<font class="striking">锁定</font>]</b></span>' : ''}`);
  359.  
  360. __comment.forEach(x => {
  361. const bbcode_html = `<div style="margin-top: 8pt; margin-bottom: 8pt;">
  362. <table id="pid${x.pid}" border="0" cellspacing="0" cellpadding="0" width="100%">
  363. <tbody>
  364. <tr>
  365. <td class="embedded" width="99%">
  366. <a href="forums.php?action=viewtopic&amp;topicid=${x.topicid}&amp;page=p${x.pid}#pid${x.pid}">#${x.pid}</a>
  367. <!-- 论坛应该不能匿名发帖吧 -->
  368. <span class="nowrap"><a href="userdetails.php?id=${x.userid}"><b>
  369. <bdo dir="ltr">${x.username}</bdo></b></a></span>&nbsp;
  370. <time>${x.edit_time.replace('T', ' ')}</time>
  371. </td>
  372. <td class="embedded nowrap" width="1%">
  373. <font class="big">#<b>${pidListSet.findIndex((a) => a == x.pid) + 1}</b> 楼&nbsp;&nbsp;</font>
  374. <a href="#top"><img class="top" src="pic/trans.gif" alt="Top" title="${lang['back_to_top']}"></a>&nbsp;&nbsp;
  375. </td>
  376. </tr>
  377. </tbody>
  378. </table>
  379. </div>
  380. <table class="main-inner" border="1" cellspacing="0" cellpadding="5">
  381. <tbody>
  382. <tr>
  383. <td class="rowfollow" width="150" valign="top" align="left" style="padding: 0px">
  384. <img src="//u2.dmhy.org/pic/default_avatar.png" alt="avatar" width="150px">
  385. </td>
  386. <td class="rowfollow" valign="top"><br>
  387. <div class="post-body" id="pid${x.pid}body">
  388. <span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">
  389. ${(() => {
  390. if (x.action === 'edit') {
  391. return `${bbcode2html(x.bbcode)}</bdo></span>
  392. ${(() => {
  393. if ($('#locale_selection').val() === 'en_US') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  394. else if ($('#locale_selection').val() === 'ru_RU') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  395. else return `<p class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </p><br><br>`;
  396. })()}`;
  397. } else {
  398. return `${bbcode2html(x.bbcode)}<br><br></bdo></span>`;
  399. };
  400. })()}
  401. </div>
  402. </td>
  403. </tr>
  404. </tbody>
  405. </table>`
  406.  
  407. if (counts[x.pid] > 1) {
  408. // console.log('有编辑记录 直接添加下拉菜单');
  409. // 插入下拉菜单基本框架
  410. if ($(`#history_comment${x.pid}_select`).length === 0) {
  411. $('#comments').append(bbcode_html); // 先插入整体框架
  412. console.log('添加下拉菜单基本框架');
  413. $(`[id="pid${x.pid}"]`).find('td:last').before(`<td class="embedded nowrap" width="1%"><a href="javascript:void(0)" class="diff_comment_button_close" style="display:none;">关闭对比</a><a href="javascript:void(0)" class="diff_comment_button_open">差异对比</a>&nbsp;&nbsp;<select name="type" id="history_comment${x.pid}_select" style="margin-top: 1px;"></select>&nbsp;&nbsp;</td>`);
  414.  
  415. };
  416. // 向下拉菜单写入信息
  417. $(`#history_comment${x.pid}_select`).append(`<option value="${x.self}">${x.edit_time.replace('T', ' ')}
  418. ${(() => { return x.action === 'edit' ? ' E' : x.action === 'reply' ? ' R' : ' N' })()}
  419. ${(() => { return x.username === null && x.userid === null ? lang['anonymous_user'] : ` ${x.username}(${x.userid})` })()}
  420. </option>`)
  421. } else {
  422. $('#comments').append(bbcode_html);
  423. };
  424. });
  425.  
  426. diffHistoryCommentBbcode(__comment, 'forum');
  427.  
  428. $("[id^=history_comment]").change(function () { // 监听菜单选择
  429. let self = $(this).val();
  430. for (let i = 0, len = __comment.length; i < len; i++) {
  431. if (self != __comment[i].self) continue;
  432. let html;
  433. let x = __comment[i];
  434. if (x.action === 'edit') {
  435. html = `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo></span>
  436. ${(() => {
  437. if ($('#locale_selection').val() === 'en_US') return `<p><font class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</font></p>`;
  438. else if ($('#locale_selection').val() === 'ru_RU') return `<p><font class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</font></p>`;
  439. else return `<br><p><font class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </font></p>`;
  440. })()}`;
  441. } else {
  442. html = `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo></span>`;
  443. };
  444. $(this).parents('[id^=pid]').parent().next().find('.post-body').html(html);
  445. return;
  446. };
  447. });
  448.  
  449. } else {
  450. console.log('获取论坛评论错误');
  451. $('#outer').find('td.text').html(`${errorstr}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>${lang['history_text_error']}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a id="apifailure" href="javascript:void(0);" style="color:#FF1212">${lang['reset_token']}</a></i>`);
  452. $("#apifailure").click(function () {
  453. let confirm = prompt("输入 YES 确认本次操作 (大写)");
  454. if (confirm === 'YES') {
  455. db.removeItem('key');
  456. db.removeItem('token');
  457. alert("成功");
  458. };
  459. });
  460. };
  461. },
  462. error: function (d) {
  463.  
  464. },
  465. });
  466. };
  467.  
  468. async function forumCommentHistory() {
  469. db = localforage.createInstance({ name: "history" });
  470.  
  471. $.ajax({
  472. type: 'post',
  473. url: 'https://u2.kysdm.com/api/v1/comment',
  474. contentType: "application/json",
  475. dataType: 'json',
  476. data: JSON.stringify({ "uid": user_id, "token": token, "topicid": topicid, "type": "forum" }),
  477. success: async function (d) {
  478. if (d.msg === 'success') {
  479. console.log('获取论坛评论成功');
  480. let __comment = d.data.comment[topicid].sort((a, b) => b.self - a.self);
  481. let pid_list = __comment.map(x => x.pid);
  482. let counts = new Object();
  483. pid_list.forEach(x => counts[x] = counts[x] ? counts[x] + 1 : 1);
  484.  
  485. let pid_list_unique = Array.from(new Set(pid_list)).sort((a, b) => a - b); // 去重排序
  486. console.log(pid_list_unique);
  487. let p = $('#outer').find('p[align="center"]:first').text().replace(/\n/g, '<br>');
  488. let pg = /(?<p>\d+)<br>$/i.exec(p);
  489. let page_total = Number(pg.groups.p); // 有多少页评论
  490. let page_now = Number($('#outer').find('p:first').find('.gray b:last').text());
  491. console.log(`现在在评论第 ${page_now} | ${page_total}`);
  492. var pid_each = await db.getItem('forum_pid_each') || 0;; // 每页最大显示楼层数量 <当数据库没有值时,pid_list_valid会是空值,>
  493.  
  494. if (page_now < page_total) {
  495. // 评论完整填满一个页面时,计算单个页面最大显示评论数量
  496. // 后期改动最大显示评论数量的数值后,直接进入帖子最后一页可能会出现不属于当前页面的评论
  497. console.log(`页面评论数量达到最大值`);
  498. pid_each = $('table[id^=pid]').length;
  499. await db.setItem('forum_pid_each', pid_each);
  500. };
  501.  
  502. var pid_list_valid = pid_list_unique.slice(pid_each * page_now - pid_each, pid_each * page_now); // 截取属于当前页面的PID
  503. console.log(pid_list_valid);
  504.  
  505. __comment.forEach(x => {
  506. let del_tag = 1;
  507. $('[id^="pid"]').each(function () {
  508. let pid = $(this).find('[class="embedded"]').children('a').first().text().replace('#', ''); // 获取网页上每个评论的PID
  509. if (x.pid == pid) {
  510. del_tag = 0; // 标记网页上有对应的PID
  511. if (counts[pid] > 1) {
  512. if ($(`#history_comment${pid}_select`).length === 0) $(this).find('td:last').before(`<td class="embedded nowrap" width="1%"><a href="javascript:void(0)" class="diff_comment_button_close" style="display:none;">关闭对比</a><a href="javascript:void(0)" class="diff_comment_button_open">差异对比</a>&nbsp;&nbsp;<select name="type" id="history_comment${x.pid}_select" style="margin-top: 1px;"></select>&nbsp;&nbsp;</td>`);
  513. $(`#history_comment${pid}_select`).append(`<option value="${x.self}">${x.edit_time.replace('T', ' ')}
  514. ${(() => { return x.action === 'edit' ? ' E' : x.action === 'reply' ? ' R' : ' N' })()}
  515. ${(() => { return x.username === null && x.userid === null ? lang['anonymous_user'] : ` ${x.username}(${x.userid})` })()}
  516. </option>`);
  517. };
  518. };
  519. });
  520.  
  521. if (del_tag === 1) {
  522. // console.log(`${x.pid} | 被删除`);
  523. if (!pid_list_valid.includes(x.pid)) return; // 不属于当前页面的PID直接跳出
  524. // 只看该作者 启用时,仅还原改用户的记录
  525. let em = /authorid=(?<authorid>\d{1,5})/i.exec(location.search);
  526. if (em && x.userid != em.groups.authorid) return true;
  527.  
  528. if ($('[id="pid10000000000"]').length === 0) {
  529. $('[id="outer"]').find('td.text:first').append(
  530. `<div style="margin-top: 8pt; margin-bottom: 8pt; display:none;">
  531. <table id="pid10000000000" border="0" cellspacing="0" cellpadding="0" width="100%">
  532. <tbody>
  533. <tr>
  534. <td class="embedded" width="99%">
  535. <a href="javascript:void(0);">#10000000000</a>
  536. </td>
  537. </tr>
  538. </tbody>
  539. </table>
  540. </div>`
  541. );
  542. };
  543.  
  544. $('[id^="pid"]').each(function () {
  545. let pid = $(this).find('[class="embedded"]').children('a').first().text().replace('#', ''); // 获取网页上每个评论的PID
  546.  
  547. if (x.pid < Number(pid)) {
  548.  
  549. const bbcode_html = `<div style="margin-top: 8pt; margin-bottom: 8pt;">
  550. <table id="pid${x.pid}" border="0" cellspacing="0" cellpadding="0" width="100%">
  551. <tbody>
  552. <tr>
  553. <td class="embedded" width="99%">
  554. <a href="forums.php?action=viewtopic&amp;topicid=${x.topicid}&amp;page=p${x.pid}#pid${x.pid}">#${x.pid}</a>
  555. <!-- 论坛应该不能匿名发帖吧 -->
  556. <span class="nowrap">
  557. <a href="userdetails.php?id=${x.userid}"><b>
  558. <bdo dir="ltr">${x.username}</bdo></b></a></span>&nbsp;
  559. <time>${x.edit_time.replace('T', ' ')}</time>
  560. </td>
  561. <td class="embedded nowrap" width="1%">
  562. <a href="#top"><img class="top" src="pic/trans.gif" alt="Top" title="${lang['back_to_top']}"></a>&nbsp;&nbsp;
  563. </td>
  564. </tr>
  565. </tbody>
  566. </table>
  567. </div>
  568. <table class="main-inner" border="1" cellspacing="0" cellpadding="5">
  569. <tbody>
  570. <tr>
  571. <td class="rowfollow" width="150" valign="top" align="left" style="padding: 0px">
  572. <img src="//u2.dmhy.org/pic/default_avatar.png" alt="avatar" width="150px">
  573. </td>
  574. <td class="rowfollow" valign="top"><br>
  575. <div class="post-body" id="pid${x.pid}body">
  576. <span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">
  577. ${(() => {
  578. if (x.action === 'edit') {
  579. return `${bbcode2html(x.bbcode)}</bdo></span>
  580. ${(() => {
  581. if ($('#locale_selection').val() === 'en_US') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  582. else if ($('#locale_selection').val() === 'ru_RU') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  583. else return `<p class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </p><br><br>`;
  584. })()}`;
  585. } else {
  586. return `${bbcode2html(x.bbcode)}<br><br></bdo></span>`;
  587. };
  588. })()}
  589. </div>
  590. </td>
  591. </tr>
  592. </tbody>
  593. </table>`
  594.  
  595. $(`[id="pid${pid}"]`).parent().before(bbcode_html);
  596.  
  597. if (counts[x.pid] > 1) {
  598. if ($(`#history_comment${x.pid}_select`).length === 0) $(`[id="pid${x.pid}"]`).find('td:last').before(`<td class="embedded nowrap" width="1%"><select name="type" id="history_comment${x.pid}_select"></td>`);
  599. $(`#history_comment${x.pid}_select`).append(`<option value="${x.self}">${x.edit_time.replace('T', ' ')}
  600. ${(() => { return x.action === 'edit' ? ' E' : x.action === 'reply' ? ' R' : ' N' })()}
  601. ${(() => { return x.username === null && x.userid === null ? lang['anonymous_user'] : ` ${x.username}(${x.userid})` })()}
  602. </option>`);
  603. };
  604.  
  605. return false;
  606. };
  607. });
  608.  
  609. };
  610. });
  611.  
  612. diffHistoryCommentBbcode(__comment, 'forum');
  613.  
  614. $("[id^=history_comment]").change(function () { // 监听菜单选择
  615. let self = $(this).val();
  616. for (let i = 0, len = __comment.length; i < len; i++) {
  617. if (self != __comment[i].self) continue;
  618. let html;
  619. let x = __comment[i];
  620. if (x.action === 'edit') {
  621. html = `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo></span>
  622. ${(() => {
  623. if ($('#locale_selection').val() === 'en_US') return `<p><font class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</font></p>`;
  624. else if ($('#locale_selection').val() === 'ru_RU') return `<p><font class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</font></p>`;
  625. else return `<br><p><font class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </font></p>`;
  626. })()}`;
  627. } else {
  628. html = `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo></span>`;
  629. };
  630. $(this).parents('[id^=pid]').parent().next().find('.post-body').html(html);
  631. return;
  632. };
  633. });
  634. } else {
  635. console.log('获取论坛评论错误');
  636. };
  637. },
  638. error: function (d) {
  639.  
  640. },
  641. });
  642. };
  643.  
  644.  
  645. function torrentCommentHistory() {
  646. $.ajax({
  647. type: 'post',
  648. url: 'https://u2.kysdm.com/api/v1/comment',
  649. contentType: "application/json",
  650. dataType: 'json',
  651. data: JSON.stringify({ "uid": user_id, "token": token, "torrent_id": torrent_id, "type": "torrent" }),
  652. success: function (d) {
  653. if (d.msg === 'success') {
  654. console.log('获取种子评论成功');
  655. let __comment = d.data.comment[torrent_id].sort((a, b) => b.self - a.self);
  656. let cid_list = __comment.map(x => x.cid);
  657. let counts = new Object();
  658. cid_list.forEach(x => counts[x] = counts[x] ? counts[x] + 1 : 1);
  659.  
  660. // let startcomments = $('#startcomments').text();
  661. if ($('[id^="cid"]').length === 0) {
  662. // 候选没有评论 || 通过的种子没有评论
  663. console.log('完全没有评论');
  664. var cid_list_valid = Array.from(new Set(cid_list));
  665. } else {
  666. // 有评论
  667. let x = $('#startcomments').nextAll('p:first').text();
  668. let pg = /(?<p>\d+)$/i.exec(x);
  669. let page_total = Math.ceil(pg.groups.p / 10); // 有多少页评论
  670. let cid_list_unique = Array.from(new Set(cid_list)).sort((a, b) => a - b); // 去重排序
  671. console.log(cid_list_unique);
  672. let page_now = $('#startcomments').nextAll('p:first').find('.gray:last').text();
  673. pg = /(?<p>\d+)$/i.exec(page_now);
  674. for (let i = 1; i <= page_total; i++) {
  675. if (Number(pg.groups.p) <= 10 * i) {
  676. console.log(`现在在评论第 ${i} 页`);
  677. var cid_list_valid = cid_list_unique.slice(10 * i - 10, 10 * i); // 截取属于当前页面的评论
  678. console.log(cid_list_valid);
  679. break;
  680. };
  681. };
  682. };
  683.  
  684. __comment.forEach(x => {
  685. let del_tag = 1;
  686. $('[id^="cid"]').each(function () {
  687. let cid = $(this).find('[class="embedded"]').children('a').attr('name');
  688. if (x.cid == cid) {
  689. del_tag = 0; // 标记网页上有对应的CID
  690. if (x.cid == cid && counts[cid] > 1) {
  691. if ($(`#history_comment${cid}_select`).length === 0) $(this).find('td:last').before(`<td class="embedded nowrap" width="1%"><a href="javascript:void(0)" class="diff_comment_button_close" style="display:none;">关闭对比</a><a href="javascript:void(0)" class="diff_comment_button_open">差异对比</a>&nbsp;&nbsp;<select name="type" id="history_comment${cid}_select" ></select>&nbsp;&nbsp;</td>`);
  692. $(`#history_comment${cid}_select`).append(`<option value="${x.self}">${x.edit_time.replace('T', ' ')}
  693. ${(() => { return x.action === 'edit' ? ' E' : x.action === 'reply' ? ' R' : ' N' })()}
  694. ${(() => { return x.username === null && x.userid === null ? lang['anonymous_user'] : ` ${x.username}(${x.userid})` })()}
  695. </option>`);
  696. };
  697. };
  698. });
  699.  
  700. if (del_tag === 1) {
  701. // console.log(`${x.cid} | 被删除`);
  702. if (!cid_list_valid.includes(x.cid)) return; // 不属于当前页面的评论直接跳出
  703. if ($('[id^="cid"]').length === 0) {
  704. // 所有评论都被删除
  705. console.log('所有评论都被删除');
  706. $('#outer').find('table:last').hide();
  707. $('#outer').find('table:last').prevAll('br').remove();
  708. $('#startcomments').remove();
  709. $('#outer').find('table:last').before('<br><h1 id="startcomments" align="center">用户评论</h1>');
  710. $('#outer').find('table:last').after(`<br>
  711. <table class="main" width="940" border="0" cellspacing="0" cellpadding="0">
  712. <tbody>
  713. <tr>
  714. <td class="embedded">
  715. <table width="100%" border="1" cellspacing="0" cellpadding="10">
  716. <tbody>
  717. <tr>
  718. <td class="text">
  719. <div style="margin-top: 8pt; margin-bottom: 8pt; display:none;">
  720. <table id="cid1000000000" border="0" cellspacing="0" cellpadding="0" width="100%">
  721. <tbody>
  722. <tr>
  723. <td class="embedded" width="99%">
  724. <a href="javascript:void(0);" name="1000000000">#1000000000</a>
  725. </td>
  726. </tr>
  727. </tbody>
  728. </table>
  729. </div>
  730. </td>
  731. </tr>
  732. </tbody>
  733. </table>
  734. </td>
  735. </tr>
  736. </tbody>
  737. </table>`
  738. );
  739. } else if ($('[id="cid1000000000"]').length === 0) {
  740. $('#startcomments').nextAll('table.main:first').find('.text').append(
  741. `<div style="margin-top: 8pt; margin-bottom: 8pt; display:none;">
  742. <table id="cid1000000000" border="0" cellspacing="0" cellpadding="0" width="100%">
  743. <tbody>
  744. <tr>
  745. <td class="embedded" width="99%">
  746. <a href="javascript:void(0);" name="1000000000">#1000000000</a>
  747. </td>
  748. </tr>
  749. </tbody>
  750. </table>
  751. </div>`);
  752. };
  753.  
  754. $('[id^="cid"]').each(function () {
  755. let cid = $(this).find('[class="embedded"]').children('a').attr('name'); // 获取网页上每个评论的CID
  756.  
  757. if (x.cid < Number(cid)) {
  758. if (x.userid === null && x.username === null) {
  759. var userInfo = `<span style="color: gray">&nbsp;<i>${lang['anonymous']}</i>&nbsp;</span>`
  760. } else {
  761. var userInfo = `<span style="color: gray">&nbsp;<span class="nowrap"><a href="userdetails.php?id=${x.userid}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span>`
  762. }
  763.  
  764. const bbcode_html = `<div style="margin-top: 8pt; margin-bottom: 8pt;">
  765. <table id="cid${x.cid}" border="0" cellspacing="0" cellpadding="0" width="100%">
  766. <tbody>
  767. <tr>
  768. <td class="embedded" width="99%">
  769. <a href="${cidUrl(x.torrent_id, x.cid)}" name="${x.cid}">#${x.cid}</a>
  770. ${userInfo}
  771. <span style="color: gray">&nbsp;<time>${x.edit_time.replace('T', ' ')}</time></span></span>
  772. </td>
  773. <td class="embedded nowrap" width="1%">
  774. <a href="#top"><img class="top" src="pic/trans.gif" alt="Top" title="Top"></a>&nbsp;&nbsp;
  775. </td>
  776. </tr>
  777. </tbody>
  778. </table>
  779. </div>
  780. <table class="main-inner" width="100%" border="0" cellspacing="0" cellpadding="5">
  781. <tbody>
  782. <tr>
  783. <td class="rowfollow" width="150" valign="top" style="padding: 0">
  784. <img src="//u2.dmhy.org/pic/default_avatar.png" alt="avatar" width="150px"></td>
  785. <td class="rowfollow" valign="top"><br>
  786. ${(() => {
  787. if (x.action === 'edit') {
  788. return `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo></span>
  789. ${(() => {
  790. if ($('#locale_selection').val() === 'en_US') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  791. else if ($('#locale_selection').val() === 'ru_RU') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  792. else return `<p class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </p><br><br>`;
  793. })()}`;
  794. } else {
  795. return `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}<br><br></bdo></span>`;
  796. };
  797. })()}
  798. </td>
  799. </tr>
  800. </tbody>
  801. </table>`;
  802.  
  803. $(`[id="cid${cid}"]`).parent().before(bbcode_html); // 先插入整体框架
  804.  
  805. if (counts[x.cid] > 1) {
  806. // console.log('有编辑记录 直接添加下拉菜单');
  807. // 插入下拉菜单基本框架
  808. if ($(`#history_comment${x.cid}_select`).length === 0) {
  809. console.log('添加下拉菜单基本框架');
  810. $(`[id="cid${x.cid}"]`).find('td:last').before(`<td class="embedded nowrap" width="1%"><select name="type" id="history_comment${x.cid}_select" ></select>&nbsp;&nbsp;</td>`);
  811. };
  812. // 向下拉菜单写入信息
  813. $(`#history_comment${x.cid}_select`).append(`<option value="${x.self}">${x.edit_time.replace('T', ' ')}
  814. ${(() => { return x.action === 'edit' ? ' E' : x.action === 'reply' ? ' R' : ' N' })()}
  815. ${(() => { return x.username === null && x.userid === null ? lang['anonymous_user'] : ` ${x.username}(${x.userid})` })()}
  816. </option>`);
  817. };
  818.  
  819. return false;
  820. };
  821.  
  822. });
  823. };
  824.  
  825. });
  826.  
  827. diffHistoryCommentBbcode(__comment, 'torrent');
  828.  
  829. $("[id^=history_comment]").change(function () { // 监听菜单选择
  830. let self = $(this).val();
  831.  
  832. for (let i = 0, len = __comment.length; i < len; i++) {
  833. if (self != __comment[i].self) continue;
  834. let html;
  835. let x = __comment[i];
  836. if (x.action === 'edit') {
  837. html = `<br>
  838. <span style="word-break: break-all; word-wrap: break-word;">
  839. <bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo>
  840. </span>
  841. ${(() => {
  842. if ($('#locale_selection').val() === 'en_US') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</p>`;
  843. else if ($('#locale_selection').val() === 'ru_RU') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</p>`;
  844. else return `<br><p class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </p>`;
  845. })()}`;
  846.  
  847. } else {
  848. html = `<br>
  849. <span style="word-break: break-all; word-wrap: break-word;">
  850. <bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo>
  851. </span>`;
  852. };
  853. $(this).parents('[id^=cid]').parent().next().find('[class="rowfollow"]:last').html(html);
  854. return;
  855. };
  856. });
  857.  
  858. } else {
  859. console.log('获取种子评论错误');
  860. };
  861. },
  862. error: function (d) {
  863.  
  864. },
  865. });
  866. };
  867.  
  868.  
  869. async function torrentInfoHistory() {
  870. if ($('h3').length === 1) { // 插入 select 基本框架
  871. const right = ($('#outer').width() - $('h3').next().width()) / 2 + 5; // 计算偏移量
  872. $('#top').after('<div id="hsty" style="position: relative;"><div id="history" style="position: absolute; right:' + right + 'px; margin-top: 4px;"><select name="type" id="history_select"></div></div>');
  873. $(window).resize(function () { $('#history').css("right", ($('#outer').width() - $('h3').next().width()) / 2 + 5); });
  874. } else {
  875. const right = ($('#outer').width() - $('#top').next().width()) / 2 + 5; // 计算偏移量
  876. $('#top').after('<div id="history" style="position: relative; float: right; margin-bottom: 10px; margin-right: ' + right + 'px"><select name="type" id="history_select"></div>');
  877. $(window).resize(function () { $('#history').css("margin-right", ($('#outer').width() - $('#history').next().width()) / 2 + 5 + 'px'); });
  878. };
  879.  
  880. $("#history_select").append("<option>" + lang['history_select_loading'] + "</option>");
  881.  
  882. const __json = await getapi(); // 从 API 获取数据
  883.  
  884. if (__json.msg !== 'success') { // 加载失败时
  885. console.log('获取历史记录失败.');
  886. $("#history_select").empty(); // 插入前先清空 option
  887. $("#history_select").append('<option value="80000">' + lang['history_select_error'] + '</option>'); // 希望你不要看到这个 (ノДT)
  888. $("#history_select").append(`<option value="90000">${lang['reset_token']}</option>`); // 删除本地授权信息
  889. $("#history_select").change(function () { // 监听菜单选择
  890. let self = Number($(this).val());
  891. if (self === 90000) {
  892. let confirm = prompt("输入 YES 确认本次操作 (大写)");
  893. if (confirm === 'YES') {
  894. db.removeItem('key');
  895. db.removeItem('token');
  896. $('#history_select').val(80000); // 将焦点设置到 80000
  897. $('#history_select').change(); // 手动触发列表更改事件
  898. alert("成功");
  899. } else {
  900. $('#history_select').val(80000); // 将焦点设置到 80000
  901. $('#history_select').change(); // 手动触发列表更改事件
  902. };
  903. };
  904. });
  905. return;
  906. };
  907.  
  908. let history_data = __json.data.history;
  909.  
  910. for (let i = 0, len = history_data.length; i < len; i++) { // 循环插入到选择列表中
  911.  
  912. if (i === 0) {
  913. $("td[class='rowhead nowrap']:contains(" + lang['description'] + ")").closest('tr').after(`<tr>
  914. <td class="rowhead nowrap" valign="top" align="right">
  915. <a href="javascript:void(0)"><span class="nowrap">
  916. <img class="plus" src="pic/trans.gif" alt="Show/Hide" id="codedescr" title="显示&nbsp;/&nbsp;隐藏"> 代码</span></a></td>
  917. <td class="rowfollow" valign="top" align="left"><a href="javascript:void(0)" id="codedescrcopy"><b>点击复制到剪贴板</b></a>
  918. <span id="codedescrcopy_text_success" style="color:#4169E1; display: none;">&nbsp;&nbsp;成功</span>
  919. <span id="codedescrcopy_text_failure" style="color:#FF0000; display: none;">&nbsp;&nbsp;失败 - 可能是你的浏览器太古老了</span>
  920. <div id="cdescr" style="display: none;">
  921. <br>
  922. <textarea class="bbcode" cols="100" style="width: 99%" id="ctdescr" rows="20"></textarea>
  923. </div>
  924. </td></tr>`
  925. )
  926. .after(`<tr id="diff_unit" >
  927. <td rowspan="2" class="no-top-bottom-border" style="font-weight: bold; text-align: right; vertical-align: top;">
  928. <a href="javascript:void(0)"><span class="nowrap">
  929. <img class="plus" src="pic/trans.gif" alt="Show/Hide" id="diffdescr" title="显示&nbsp;/&nbsp;隐藏"> 差异</span></a></td>
  930. </td>
  931. <td valign="top" align="left" class="no-top-bottom-border">
  932. <div class="diff-container" style="display: none;">
  933. <div class="diff-cell">&nbsp;<select name="type" id="history_select2"></select></div>
  934. <div class="diff-cell"><select name="type" id="history_select3"></select></div>
  935. </div>
  936. </td>
  937. </tr>
  938. <tr id="diff_draw_unit">
  939. <td valign="top" align="left" class="no-top-bottom-border">
  940. <div id="diff_draw" class="draw-div" style="display: none;"></div>
  941. </td>
  942. </tr>`);
  943.  
  944. $(`td[class='rowhead nowrap']:contains('${lang['torrent_info']}')`).next('td').find('.no_border_wide:last')
  945. .before(`<td id="torrent_ver" class="no_border_wide"></td>`)
  946. .before(`<td id="torrent_piece_length" class="no_border_wide"></td>`);
  947. $('#torrent_ver').html(`<b>${lang['torrent_ver']}:</b>&nbsp;${history_data[i].torrent_ver}`);
  948. const numberOfPieces = Math.ceil(history_data[i].torrent_size / history_data[i].torrent_piece_length);
  949. $('#torrent_piece_length').html(`<b>${lang['torrent_piece_length']}:</b>&nbsp;${numberOfPieces} (${convertBytesToAutoUnit(history_data[i].torrent_piece_length)})`);
  950. $('#ctdescr').val(history_data[i].description_info);
  951. $('#codedescr').closest('a').click(function () {
  952. $('#cdescr').toggle();
  953. $('#codedescr').attr('class', $('#codedescr').attr('class') === 'plus' ? 'minus' : 'plus');
  954. });
  955. $('#diffdescr').closest('a').click(async function () {
  956. $('#diff_draw, .diff-container').toggle();
  957. if ($('#diffdescr').attr('class') === 'plus') {
  958. $('#diffdescr').attr('class', 'minus');
  959. await db.setItem('diff_switch', true);
  960. } else {
  961. $('#diffdescr').attr('class', 'plus');
  962. await db.setItem('diff_switch', false);
  963. }
  964. });
  965.  
  966. // 记录上次差异按钮打开状态
  967. if (await db.getItem('diff_switch')) $('#diffdescr').closest('a').trigger('click');
  968.  
  969. $('#codedescrcopy').click(function () {
  970. const _val = $('#ctdescr').val();
  971. navigator.clipboard.writeText(_val).then(() => {
  972. $('#codedescrcopy_text_success').fadeIn(500);
  973. $('#codedescrcopy_text_success').fadeOut(1000);
  974. }).catch(() => {
  975. $('#codedescrcopy_text_failure').fadeIn(500);
  976. $('#codedescrcopy_text_failure').fadeOut(1000);
  977. });
  978. });
  979. };
  980.  
  981. $("#history_select, #history_select2, #history_select3").append("<option value='" + history_data[i].self + "'>"
  982. + history_data[i].get_time.replace('T', ' ')
  983. + ((edited_type) => {
  984. switch (edited_type) {
  985. case 0: return ' H'; // 添加候选
  986. case 1: return ' E'; // 普通用户编辑
  987. case 2: return ' M'; // MOD编辑
  988. case 3: return ' T'; // 允许候选
  989. case 4: return ' U'; // 上传种子
  990. case 5: return ' R'; // 还原被删除的种子
  991. default: return ' '; // 早期记录
  992. };
  993. })(history_data[i].edited_type)
  994. + (() => {
  995. if (history_data[i].self === 0) return lang['current_time']
  996. else if (history_data[i].edited_name === null && history_data[i].edited_id === null) return ''
  997. else if (history_data[i].edited_id === null && history_data[i].edited_name === '匿名') return lang['anonymous_user']
  998. else if (history_data[i].edited_id === null && history_data[i].edited_name === '系统') return lang['system']
  999. else if (history_data[i].edited_name !== null && history_data[i].edited_id !== null) return ' ' + history_data[i].edited_name + '(' + history_data[i].edited_id + ')'
  1000. else return ' @BUG@'
  1001. })()
  1002. + "</option>");
  1003. };
  1004.  
  1005. // 草 为什么会这样呢 明明原来很整齐的
  1006. $("#history_select").change(function () { // 监听菜单选择
  1007. let self = Number($(this).val());
  1008. for (let i = 0, len = history_data.length; i < len; i++) {
  1009. if (self !== history_data[i].self) continue;
  1010. history_data[i].banned === 1 ? $('#top').html(history_data[i].title + '&nbsp;&nbsp;&nbsp; <b>[<font class="striking">' + lang['banned'] + '</font>]</b>') : $('#top').text(history_data[i].title);
  1011. // 检查副标题一栏是否存在
  1012. if ($("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").length === 0 && history_data[i].subtitle !== null) {
  1013. $("td[class='rowhead nowrap']:contains(" + lang['uploaded'] + ")").parent().before('<tr><td class="rowhead nowrap" valign="top" align="right">' + lang['subtitle'] + '</td><td class="rowfollow" valign="top" align="left"></td></tr>');
  1014. }
  1015. else if ($("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").length === 1 && history_data[i].subtitle === null) {
  1016. $("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").parent().remove();
  1017. };
  1018.  
  1019. $("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").next().text(history_data[i].subtitle); // 副标题
  1020. $("td[class='rowhead nowrap']:contains(" + lang['description'] + ")").last().next().html('<div id="kdescr"><span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">' + bbcode2html(history_data[i].description_info) + '</bdo></span></div>'); // 描述
  1021. $('#ctdescr').val(history_data[i].description_info); // 描述代码
  1022. $('#torrent_ver').html(`<b>${lang['torrent_ver']}:</b>&nbsp;${history_data[i].torrent_ver}`); // 版本
  1023. const numberOfPieces = Math.ceil(history_data[i].torrent_size / history_data[i].torrent_piece_length);
  1024. $('#torrent_piece_length').html(`<b>${lang['torrent_piece_length']}:</b>&nbsp;${numberOfPieces} (${convertBytesToAutoUnit(history_data[i].torrent_piece_length)})`); // 区块
  1025.  
  1026. if ($('h3').length === 1) { // 已经通过候选的种子
  1027. $("td[class='rowhead nowrap']:contains(" + lang['uploaded'] + ")").next().html(((p) => {
  1028. if (p.uploader_id === null && p.uploader_name === '匿名') return '<i>' + lang['anonymous'] + '</i>'; // 匿名发布
  1029. if (p.uploader_id === null && p.uploader_name !== '匿名') return p.uploader_name; // 自定义署名 不带UID
  1030. if (p.uploader_id !== null && p.uploader_name !== '匿名') return '<a href="userdetails.php?id=' + p.uploader_id + '"><b>' + p.uploader_name + '</b></a>'; // 正常显示 || 自定义署名 带UID
  1031. })(history_data[i])); // 发布人
  1032. $("td[class='rowhead nowrap']:contains(" + lang['basic_info'] + ")").next().html('<b>' + lang['uploaded_at'] + ':</b> ' + history_data[i].uploaded_at.replace('T', ' ')
  1033. + (() => { if (history_data[i].torrent_size) { return '&nbsp;&nbsp;&nbsp;<b>' + lang['size'] + ':</b>&nbsp;' + convert(history_data[i].torrent_size) } else { return ''; } })()
  1034. + '&nbsp;&nbsp;&nbsp;<b>' + lang['category'] + ':</b> ' + history_data[i].category)
  1035. } else { // 还在候选的种子
  1036. $("td[class='rowhead nowrap']:contains(" + lang['basic_info'] + ")").next().html('<b>' + lang['submitted_by'] + '</b>:&nbsp;'
  1037. + ((p) => {
  1038. if (p.uploader_id === null && p.uploader_name === '匿名') return '<i>' + lang['anonymous'] + '</i>'; // 匿名发布
  1039. if (p.uploader_id !== null && p.uploader_name !== '匿名') return '<a href="userdetails.php?id=' + p.uploader_id + '"><b>' + p.uploader_name + '</b></a>'; // 正常显示
  1040. })(history_data[i])
  1041. + '&nbsp;&nbsp;&nbsp;<b>' + lang['submitted_at'] + '</b>:&nbsp;<time>'
  1042. + history_data[i].uploaded_at.replace('T', ' ')
  1043. + '</time>&nbsp;&nbsp;&nbsp;<b>'
  1044. + (() => { if (history_data[i].torrent_size) { return `${lang['size']}</b>:&nbsp;${convert(history_data[i].torrent_size)}&nbsp;&nbsp;&nbsp;<b>` } else { return ''; } })()
  1045. + lang['category'] + '</b>:&nbsp;'
  1046. + history_data[i].category
  1047. );
  1048. };
  1049. };
  1050. });
  1051.  
  1052. $("#history_select2, #history_select3").change(function () {
  1053. const leftValue = Number($("#history_select2").val());
  1054. const rightValue = Number($("#history_select3").val());
  1055. drawDiffHistoryBbcode(history_data, leftValue, rightValue, 'torrent', $('#diff_draw'))
  1056. });
  1057.  
  1058. const $historySelect2 = $("#history_select2");
  1059. const $historySelect3 = $("#history_select3");
  1060. // const firstOptionText = $historySelect2.find("option:eq(0)").text();
  1061. const historySelect2OptionsLength = $historySelect2.find("option").length;
  1062.  
  1063. if (historySelect2OptionsLength === 1) {
  1064. // 就一个记录,无法进行差异处理
  1065. $('#diff_draw_unit, #diff_unit').hide();
  1066. } else {
  1067. let rightValue = 0;
  1068. let leftValue = 1;
  1069. let flagBreak = false;
  1070.  
  1071. while (leftValue < historySelect2OptionsLength) {
  1072. if (preCheckBbcodeDiscrepancy(history_data, leftValue, rightValue) === true) {
  1073. $historySelect3.find("option").eq(rightValue).prop("selected", true);
  1074. $historySelect2.find("option").eq(leftValue).prop("selected", true);
  1075. $historySelect2.trigger("change");
  1076. flagBreak = true;
  1077. break;
  1078. }
  1079. rightValue++;
  1080. leftValue++;
  1081. }
  1082.  
  1083. if (!flagBreak) $('#diff_draw_unit, #diff_unit').hide();
  1084.  
  1085. }
  1086.  
  1087. $("#history_select option:first").remove(); // 删除加载等待一栏
  1088.  
  1089. };
  1090.  
  1091. function torrentCommentHistoryReset() {
  1092.  
  1093. const callback = (mutations, observer) => {
  1094. mutations.forEach(function (mutation) {
  1095. if (mutation.addedNodes.length === 0) return;
  1096.  
  1097. console.log('检测到种子页面已经完成加载');
  1098. observer.disconnect(); // 停止监听
  1099.  
  1100. $('#description').after(`<br><br><h1 align="center" id="startcomments" style="font-weight:normal; font-style: italic">正在加载用户评论...</h1><br>`);
  1101. $.ajax({
  1102. type: 'post',
  1103. url: 'https://u2.kysdm.com/api/v1/comment',
  1104. contentType: "application/json",
  1105. dataType: 'json',
  1106. data: JSON.stringify({ "uid": user_id, "token": token, "torrent_id": torrent_id, "type": "torrent" }),
  1107. success: function (d) {
  1108. if (d.msg !== 'success') { console.log('获取种子评论错误'); }
  1109. else {
  1110. console.log('获取种子评论成功');
  1111. let __comment = d.data.comment[torrent_id].sort(firstBy((a, b) => a.cid - b.cid).thenBy((a, b) => b.self - a.self)); // 如果用self排序,消息顺序不正确,则改用编辑日期排序
  1112. if (__comment.length === 0) { // 没有评论
  1113. $('#startcomments').text('没有评论');
  1114. $('#startcomments').removeAttr("style");
  1115. return;
  1116. } else {
  1117. $('#startcomments').text('用户评论');
  1118. $('#startcomments').removeAttr("style");
  1119. };
  1120.  
  1121. let cidList = __comment.map(x => x.cid);
  1122. let counts = new Object();
  1123. cidList.forEach(x => counts[x] = counts[x] ? counts[x] + 1 : 1);
  1124.  
  1125. $('#startcomments').after(`<br>
  1126. <table class="main" width="940" border="0" cellspacing="0" cellpadding="0">
  1127. <tbody>
  1128. <tr>
  1129. <td class="embedded">
  1130. <table width="100%" border="1" cellspacing="0" cellpadding="10">
  1131. <tbody>
  1132. <tr>
  1133. <td id="comments" class="text"></td>
  1134. </tr>
  1135. </tbody>
  1136. </table>
  1137. </td>
  1138. </tr>
  1139. </tbody>
  1140. </table>`
  1141. );
  1142.  
  1143. __comment.forEach(x => {
  1144. if (x.userid === null && x.username === null) {
  1145. var userInfo = `<span style="color: gray">&nbsp;<i>${lang['anonymous']}</i>&nbsp;</span>`
  1146. } else {
  1147. var userInfo = `<span style="color: gray">&nbsp;<span class="nowrap"><a href="userdetails.php?id=${x.userid}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span>`
  1148. }
  1149.  
  1150. const bbcode_html = `<div style="margin-top: 8pt; margin-bottom: 8pt;">
  1151. <table id="cid${x.cid}" border="0" cellspacing="0" cellpadding="0" width="100%">
  1152. <tbody>
  1153. <tr>
  1154. <td class="embedded" width="99%">
  1155. <a href="${cidUrl(x.torrent_id, x.cid)}" name="${x.cid}">#${x.cid}</a>
  1156. ${userInfo}
  1157. <span style="color: gray">&nbsp;<time>${x.edit_time.replace('T', ' ')}</time></span></span>
  1158. </td>
  1159. <td class="embedded nowrap" width="1%">
  1160. <a href="#top"><img class="top" src="pic/trans.gif" alt="Top" title="Top"></a>&nbsp;&nbsp;
  1161. </td>
  1162. </tr>
  1163. </tbody>
  1164. </table>
  1165. </div>
  1166. <table class="main-inner" width="100%" border="0" cellspacing="0" cellpadding="5">
  1167. <tbody>
  1168. <tr>
  1169. <td class="rowfollow" width="150" valign="top" style="padding: 0">
  1170. <img src="//u2.dmhy.org/pic/default_avatar.png" alt="avatar" width="150px"></td>
  1171. <td class="rowfollow" valign="top"><br>
  1172. ${(() => {
  1173. if (x.action === 'edit') {
  1174. return `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo></span>
  1175. ${(() => {
  1176. if ($('#locale_selection').val() === 'en_US') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  1177. else if ($('#locale_selection').val() === 'ru_RU') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  1178. else return `<p class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </p><br><br>`;
  1179. })()}`;
  1180. } else {
  1181. return `<span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">${bbcode2html(x.bbcode)}<br><br></bdo></span>`;
  1182. };
  1183. })()}
  1184. </td>
  1185. </tr>
  1186. </tbody>
  1187. </table>`;
  1188.  
  1189. if (counts[x.cid] > 1) {
  1190. // console.log('有编辑记录 直接添加下拉菜单');
  1191. // 插入下拉菜单基本框架
  1192. if ($(`#history_comment${x.cid}_select`).length === 0) {
  1193. $('#comments').append(bbcode_html); // 先插入整体框架
  1194. console.log('添加下拉菜单基本框架');
  1195. $(`[id="cid${x.cid}"]`).find('[class="embedded nowrap"]').before(`<td class="embedded nowrap" width="1%"><a href="javascript:void(0)" class="diff_comment_button_close" style="display:none;">关闭对比</a><a href="javascript:void(0)" class="diff_comment_button_open">差异对比</a>&nbsp;&nbsp;<select name="type" id="history_comment${x.cid}_select" ></select>&nbsp;&nbsp;</td>`);
  1196. };
  1197. // 向下拉菜单写入信息
  1198. $(`#history_comment${x.cid}_select`).append(`<option value="${x.self}">${x.edit_time.replace('T', ' ')}
  1199. ${(() => { return x.action === 'edit' ? ' E' : x.action === 'reply' ? ' R' : ' N' })()}
  1200. ${(() => { return x.username === null && x.userid === null ? lang['anonymous_user'] : ` ${x.username}(${x.userid})` })()}
  1201. </option>`)
  1202. } else {
  1203. $('#comments').append(bbcode_html);
  1204. };
  1205. });
  1206.  
  1207. diffHistoryCommentBbcode(__comment, 'torrent');
  1208.  
  1209. $("[id^=history_comment]").change(function () { // 监听菜单选择
  1210. let self = $(this).val();
  1211. for (let i = 0, len = __comment.length; i < len; i++) {
  1212. if (self != __comment[i].self) continue;
  1213. let html;
  1214. let x = __comment[i];
  1215. if (x.action === 'edit') {
  1216. html = `<br>
  1217. <span style="word-break: break-all; word-wrap: break-word;">
  1218. <bdo dir="ltr">${bbcode2html(x.bbcode)}</bdo>
  1219. </span>
  1220. ${(() => {
  1221. if ($('#locale_selection').val() === 'en_US') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> at <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  1222. else if ($('#locale_selection').val() === 'ru_RU') return `<p class="small">${lang['last_edited']} <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> в <time>${x.edit_time.replace('T', ' ')}</time>.</p><br><br>`;
  1223. else return `<br><p class="small">[<time>${x.edit_time.replace('T', ' ')}</time>] <span class="nowrap"><a href="userdetails.php?id=${x.user_id}"><b><bdo dir="ltr">${x.username}</bdo></b></a></span> ${lang['last_edited']} </p><br><br>`;
  1224. })()}`;
  1225. } else {
  1226. html = `<br>
  1227. <span style="word-break: break-all; word-wrap: break-word;">
  1228. <bdo dir="ltr">${bbcode2html(x.bbcode)}<br><br></bdo>
  1229. </span>`;
  1230. };
  1231. $(this).parents('[id^=cid]').parent().next().find('[class="rowfollow"]:last').html(html);
  1232. return;
  1233. };
  1234. });
  1235. };
  1236. },
  1237. error: function (d) {
  1238. },
  1239. });
  1240. })
  1241. };
  1242.  
  1243. const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  1244. const element = document.getElementById("outer");
  1245. var observer = new MutationObserver(callback);
  1246. observer.observe(element, { childList: true });
  1247. };
  1248.  
  1249. async function torrentInfoHistoryReset() {
  1250. const errorstr = $('#outer').find('td.text').text();
  1251. // 正在努力加载中...
  1252. $('#outer').find('td.text').html(errorstr + '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>' + lang['history_text_loading'] + '</i>');
  1253.  
  1254. const __json = await getapi(); // 从 API 获取数据
  1255.  
  1256. if (__json.msg !== 'success') { // 加载失败时
  1257. console.log('获取历史记录失败.');
  1258. $('#outer').find('td.text').html(`${errorstr}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<i>${lang['history_text_error']}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a id="apifailure" href="javascript:void(0);" style="color:#FF1212">${lang['reset_token']}</a></i>`);
  1259. $("#apifailure").click(function () {
  1260. let confirm = prompt("输入 YES 确认本次操作 (大写)");
  1261. if (confirm === 'YES') {
  1262. db.removeItem('key');
  1263. db.removeItem('token');
  1264. alert("成功");
  1265. };
  1266. });
  1267.  
  1268. return;
  1269. } else if (__json.data.history.length === 0) { // 获取成功 但没有历史记录时
  1270. console.log('没有历史记录.');
  1271. $('#outer').find('td.text').html(errorstr + '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + lang['history_text_empty'] + '</i>');
  1272. return;
  1273. };
  1274.  
  1275. console.log('获取历史记录成功.');
  1276. let history_data = __json.data.history;
  1277. // let gdListObj = JSON.parse(localStorage.getItem("u2_gd_list")); // 读取谷歌备份列表
  1278. // 还原网页
  1279. $('#outer').html('<h1 align="center" id="top">'
  1280. + (() => { return history_data[0].banned === 1 ? history_data[0].title + '&nbsp;&nbsp;&nbsp; <b>[<font class="striking">' + lang['banned'] + '</font>]</b>' : history_data[0].title; })()
  1281. + '</h1>'
  1282. + '<div id="hsty" style="position: relative;"><div id="history" style="position: absolute; right:75px; margin-top: 4px;">'
  1283. + '<select name="type" id="history_select" style="visibility: visible;"></select></div></div>'
  1284. + '<h3>(#' + torrent_id + ')</h3>'
  1285. + '<table id="description" width="90%" min-width="940px" cellspacing="0" cellpadding="5"><tbody><tr><td class="rowhead" width="13%">' + lang['torrent_title'] + '</td>'
  1286. + '<td class="rowfollow" width="87%" align="left">'
  1287. + '<b>[U2].' + history_data[0].torrent_name + '.torrent</b></td></tr>'
  1288. + (() => { return history_data[0].subtitle ? '<tr><td class="rowhead nowrap" valign="top" align="right">' + lang['subtitle'] + '</td><td class="rowfollow" valign="top" align="left">' + history_data[0].subtitle + '</td></tr></td></tr>' : '' })()
  1289. + '<tr><td class="rowhead nowrap" valign="top" align="right">' + lang['basic_info'] + '</td>'
  1290. + '<td class="rowfollow" valign="top" align="left"><b>' + lang['submitted_by'] + '</b>:&nbsp;'
  1291. + ((p) => {
  1292. if (p.uploader_id === null && p.uploader_name === '匿名') return '<i>' + lang['anonymous'] + '</i>'; // 匿名发布
  1293. if (p.uploader_id !== null && p.uploader_name !== '匿名') return '<a href="userdetails.php?id=' + p.uploader_id + '"><b>' + p.uploader_name + '</b></a>'; // 正常显示
  1294. })(history_data[0])
  1295. + '&nbsp;&nbsp;&nbsp;<b>' + lang['submitted_at'] + '</b>:&nbsp;<time>' + history_data[0].uploaded_at.replace('T', ' ')
  1296. + '</time>'
  1297. + (() => { if (history_data[0].torrent_size) { return '&nbsp;&nbsp;&nbsp;<b>大小:</b>&nbsp;' + convert(history_data[0].torrent_size) } else { return ''; } })()
  1298. + '&nbsp;&nbsp;&nbsp;<b>' + lang['category'] + '</b>:&nbsp;' + history_data[0].category
  1299. // + (() => {
  1300. // const r = '&nbsp;&nbsp;&nbsp;<b>' + lang['google_backup'] + '</b>:&nbsp;'
  1301. // if (gdListObj === null) return ``; // 列表不存在时,直接返回
  1302. // const gdList = gdListObj.list; // 载入种子列表
  1303. // let d = gdList.findIndex((value) => value == Number(torrent_id)); // 查找数据库中是否有备份,没有返回-1
  1304. // if (d === -1) return r + `×`; // 没有备份时
  1305. // return `${r}<a href="sendmessage.php?receiver=45940#${torrent_id}" target="_blank" title="${lang['google_send']}">√</a>`
  1306. // })()
  1307. + '</td></tr>'
  1308. + '<tr><td class="rowhead nowrap" valign="top" align="right">'
  1309. + '<a href="javascript: klappe_news(\'descr\')"><span class="nowrap">'
  1310. + '<img class="minus" src="pic/trans.gif" alt="Show/Hide" id="picdescr" title="' + lang['show_or_hide'] + '"> ' + lang['description'] + '</span></a></td>'
  1311. + '<td class="rowfollow" valign="top" align="left">'
  1312. + '<div id="kdescr"><span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">'
  1313. + bbcode2html(history_data[0].description_info) + '</bdo></span></div></td></tr><tr>'
  1314. + '<td class="rowhead nowrap" valign="top" align="right">' + lang['torrent_info'] + '</td>'
  1315. + `<td id="file_tree" class="rowfollow" valign="top" align="left"></td></tr></tbody></table></td></tr></tbody></table><br><br></br>`
  1316. );
  1317.  
  1318. $("td[class='rowhead nowrap']:contains(" + lang['description'] + ")").closest('tr').after(`<tr>
  1319. <td class="rowhead nowrap" valign="top" align="right">
  1320. <a href="javascript:void(0)"><span class="nowrap">
  1321. <img class="plus" src="pic/trans.gif" alt="Show/Hide" id="codedescr" title="显示&nbsp;/&nbsp;隐藏"> 代码</span></a></td>
  1322. <td class="rowfollow" valign="top" align="left"><a href="javascript:void(0)" id="codedescrcopy"><b>点击复制到剪贴板</b></a>
  1323. <span id="codedescrcopy_text_success" style="color:#4169E1; display: none;">&nbsp;&nbsp;成功</span>
  1324. <span id="codedescrcopy_text_failure" style="color:#FF0000; display: none;">&nbsp;&nbsp;失败 - 可能是你的浏览器太古老了</span>
  1325. <div id="cdescr" style="display: none;">
  1326. <br>
  1327. <textarea class="bbcode" cols="100" style="width: 99%" id="ctdescr" rows="20"></textarea>
  1328. </div>
  1329. </td></tr>`
  1330. ).after(`<tr id="diff_unit" >
  1331. <td rowspan="2" class="no-top-bottom-border" style="font-weight: bold; text-align: right; vertical-align: top;">
  1332. <a href="javascript:void(0)"><span class="nowrap">
  1333. <img class="plus" src="pic/trans.gif" alt="Show/Hide" id="diffdescr" title="显示&nbsp;/&nbsp;隐藏"> 差异</span></a></td>
  1334. </td>
  1335. <td valign="top" align="left" class="no-top-bottom-border">
  1336. <div class="diff-container" style="display: none;">
  1337. <div class="diff-cell">&nbsp;<select name="type" id="history_select2"></select></div>
  1338. <div class="diff-cell"><select name="type" id="history_select3"></select></div>
  1339. </div>
  1340. </td>
  1341. </tr>
  1342. <tr id="diff_draw_unit">
  1343. <td valign="top" align="left" class="no-top-bottom-border">
  1344. <div id="diff_draw" class="draw-div" style="display: none;"></div>
  1345. </td>
  1346. </tr>`);
  1347.  
  1348. $('#ctdescr').val(history_data[0].description_info);
  1349. $('#codedescr').closest('a').click(function () {
  1350. $('#cdescr').toggle();
  1351. $('#codedescr').attr('class', $('#codedescr').attr('class') === 'plus' ? 'minus' : 'plus');
  1352. });
  1353. $('#diffdescr').closest('a').click(async function () {
  1354. $('#diff_draw, .diff-container').toggle();
  1355. if ($('#diffdescr').attr('class') === 'plus') {
  1356. $('#diffdescr').attr('class', 'minus');
  1357. await db.setItem('diff_switch', true);
  1358. } else {
  1359. $('#diffdescr').attr('class', 'plus');
  1360. await db.setItem('diff_switch', false);
  1361. }
  1362. });
  1363.  
  1364. // 记录上次差异按钮打开状态
  1365. if (await db.getItem('diff_switch')) $('#diffdescr').closest('a').trigger('click');
  1366.  
  1367. $('#codedescrcopy').click(function () {
  1368. const _val = $('#ctdescr').val();
  1369. navigator.clipboard.writeText(_val).then(() => {
  1370. $('#codedescrcopy_text_success').fadeIn(500);
  1371. $('#codedescrcopy_text_success').fadeOut(1000);
  1372. }).catch(() => {
  1373. $('#codedescrcopy_text_failure').fadeIn(500);
  1374. $('#codedescrcopy_text_failure').fadeOut(1000);
  1375. });
  1376. });
  1377.  
  1378. const putFileTree = (index) => {
  1379. // 插入ID
  1380. let counter = 0;
  1381. const InsertSeq = (data) => {
  1382. for (key in data) {
  1383. data[key]['id'] = counter;
  1384. counter++;
  1385. if (data[key]['type'] == 'directory') InsertSeq(data[key]['children']);
  1386. };
  1387. return data;
  1388. };
  1389. // 获取文件ID
  1390. const getFile = (data) => {
  1391. let a = []
  1392. for (key in data) {
  1393. if (data[key]['type'] == 'file') a.push(data[key]['id']);
  1394. };
  1395. a = a.concat(getDirectory(data));
  1396. return a;
  1397. };
  1398. // 获取文件夹ID
  1399. const getDirectory = (data, directory = []) => {
  1400. for (key in data) {
  1401. if (data[key]['type'] == 'directory') directory.push(data[key]['id']);
  1402. };
  1403. return directory;
  1404. };
  1405. // 获取文件体积
  1406. const getSize = (data, size = 0) => {
  1407. // console.log(data);
  1408. for (key in data) {
  1409. if (data[key]['type'] == 'file') {
  1410. size = size + data[key]['length'];
  1411. } else {
  1412. size = getSize(data[key]['children'], size);
  1413. };
  1414. }
  1415. return size;
  1416. };
  1417. // 遍历JSON
  1418. const tree = (j, i) => {
  1419. for (let key in j) {
  1420. // console.log(j);
  1421. // console.log(j['key']);
  1422. if (j[key]['type'] == 'directory') {
  1423. let children = j[key]['children'];
  1424. let f_id_sh = getFile(children); // 获取文件夹需要的id
  1425. let f_size = convert(getSize(children)) // 文件夹大小
  1426. if (f_id === 0) {
  1427. f_html = f_html + `<tr id="f_id_0" tag="closed"><td class="rowfollow"><a href="javascript:void(0)" onclick="showorhide([${f_id_sh}],0)" class="faqlink">${key}</a></td><td class="rowfollow dir_size" align="right">[${f_size}]</td></tr>`;
  1428. } else {
  1429. f_html = f_html + `<tr id="f_id_${f_id}" style="display: none;" tag="closed"><td class="rowfollow">${space.repeat(i)}<a href="javascript:void(0)" onclick="showorhide([${f_id_sh}],${f_id})" class="faqlink">${key}</a></td><td class="rowfollow dir_size" align="right">[${f_size}]</td></tr>`;
  1430. };
  1431. f_id++;
  1432. tree(children, i + 1);
  1433. }
  1434. else {
  1435. if (f_id === 0) {
  1436. // 单文件种子
  1437. f_html = f_html + `<tr id="f_id_${f_id}"><td class="rowfollow">${space.repeat(i)}${key}</td><td class="rowfollow" align="right">${convert(j[key]['length'])}</td></tr>`;
  1438. } else {
  1439. f_html = f_html + `<tr id="f_id_${f_id}" style="display: none;"><td class="rowfollow">${space.repeat(i)}${key}</td><td class="rowfollow" align="right">${convert(j[key]['length'])}</td></tr>`;
  1440. };
  1441. f_id++;
  1442. };
  1443. };
  1444. };
  1445.  
  1446. let torrent_tree = history_data[index].torrent_tree;
  1447. const numberOfPieces = Math.ceil(history_data[0].torrent_size / history_data[0].torrent_piece_length);
  1448.  
  1449. if (torrent_tree === null) {
  1450. // console.log('tree 为空');
  1451. $('#file_tree').html(
  1452. `<table>
  1453. <tbody>
  1454. <tr>
  1455. <td class="no_border_wide"><b>${lang['files']}</b>: ${history_data[0].torrent_files_qty}<br></td>
  1456. <td class="no_border_wide"><b>${lang['info_hash']}:</b>&nbsp;${history_data[0].torrent_hash}</td>
  1457. <td id="torrent_ver" class="no_border_wide"><b>${lang['torrent_ver']}:</b>&nbsp;${history_data[0].torrent_ver}</td>
  1458. <td id="torrent_piece_length" class="no_border_wide"><b>${lang['torrent_piece_length']}:</b>&nbsp;${numberOfPieces} (${convertBytesToAutoUnit(history_data[0].torrent_piece_length)})</td>
  1459. </tr>
  1460. </tbody>
  1461. </table>`
  1462. );
  1463. return;
  1464. };
  1465.  
  1466. torrent_tree = stringify(torrent_tree, function (a, b) {
  1467. // 对keys排序
  1468. if (typeof (a.value) !== 'object' || typeof (b.value) !== 'object') return 0;
  1469. if (a.value.type === 'directory' && b.value.type === 'file') {
  1470. return -1;
  1471. } else if (a.value.type === 'file' && b.value.type === 'directory') {
  1472. return 1;
  1473. } else {
  1474. return a.key.toLowerCase() < b.key.toLowerCase() ? -1 : 1;
  1475. };
  1476. });
  1477. torrent_tree = JSON.parse(torrent_tree);
  1478. torrent_tree = InsertSeq(torrent_tree);
  1479. // console.log(__json);
  1480. let f_id = 0; // 元素id
  1481. let f_html = ''; // 文件列表
  1482. const space = '&nbsp;&nbsp;&nbsp;&nbsp;'; // 缩进
  1483. tree(torrent_tree, 0);
  1484. // $('#filelist').find('tr').after(f_html);
  1485. $('#file_tree').html(
  1486. `<table>
  1487. <tbody>
  1488. <tr>
  1489. <td class="no_border_wide"><b>${lang['files']}</b>: ${history_data[index].torrent_files_qty}<br>
  1490. <span id="showfl" style="display: inline;">
  1491. <a href="javascript: viewfilelist()">[查看列表]</a>
  1492. </span>
  1493. <span id="hidefl" style="display: none;">
  1494. <a href="javascript: hidefilelist()">[隐藏列表]</a>
  1495. </span>
  1496. ${(() => {
  1497. return f_id > 1
  1498. ? `<span id="expandall" style="display: none;"><a href="javascript: expandall(true)">[全部展开]</a></span>
  1499. <span id="closeall" style="display: none;"><a href="javascript: expandall(false)">[全部关闭]</a></span>`
  1500. : ''
  1501. })()}
  1502. </td>
  1503. <td class="no_border_wide"><b>${lang['info_hash']}:</b>&nbsp;${history_data[index].torrent_hash}</td>
  1504. <td id="torrent_ver" class="no_border_wide"><b>${lang['torrent_ver']}:</b>&nbsp;${history_data[index].torrent_ver}</td>
  1505. <td id="torrent_piece_length" class="no_border_wide"><b>${lang['torrent_piece_length']}:</b>&nbsp;${numberOfPieces} (${convertBytesToAutoUnit(history_data[0].torrent_piece_length)})</td>
  1506. </tr>
  1507. </tbody>
  1508. </table>
  1509. <span id="filelist" style="display: none;">
  1510. <style>
  1511. .dir_size {
  1512. color: gray;
  1513. white-space: nowrap;
  1514. }
  1515. </style>
  1516. <table border="1" cellspacing="0" cellpadding="5">
  1517. <tbody>
  1518. <tr>
  1519. <td class="colhead">路径</td>
  1520. <td class="colhead" align="center"><img class="size" src="pic/trans.gif" alt="size"></td>
  1521. </tr>
  1522. ${f_html}
  1523. </tbody>
  1524. </table>
  1525. </span>`
  1526. );
  1527. };
  1528. putFileTree(0); // 运行一次,生成列表
  1529.  
  1530. for (let i = 0, len = history_data.length; i < len; i++) { // 循环插入到选择列表中
  1531. $("#history_select, #history_select2, #history_select3").append("<option value='" + history_data[i].self + "'>"
  1532. + history_data[i].get_time.replace('T', ' ')
  1533. + ((edited_type) => {
  1534. switch (edited_type) {
  1535. case 0: return ' H'; // 添加候选
  1536. case 1: return ' E' // 普通用户编辑
  1537. case 2: return ' M' // MOD编辑
  1538. case 3: return ' T' // 允许候选
  1539. case 4: return ' U' // 上传种子
  1540. case 5: return ' R'; // 还原被删除的种子
  1541. default: return ' ' // 早期记录
  1542. };
  1543. })(history_data[i].edited_type)
  1544. + (() => {
  1545. if (history_data[i].self === 0) return lang['current_time']
  1546. else if (history_data[i].edited_name === null && history_data[i].edited_id === null) return ''
  1547. else if (history_data[i].edited_id === null && history_data[i].edited_name === '匿名') return lang['anonymous_user']
  1548. else if (history_data[i].edited_id === null && history_data[i].edited_name === '系统') return lang['system']
  1549. else if (history_data[i].edited_name !== null && history_data[i].edited_id !== null) return ' ' + history_data[i].edited_name + '(' + history_data[i].edited_id + ')'
  1550. else return ' @BUG@'
  1551. })()
  1552. + "</option>");
  1553. };
  1554.  
  1555. $("#history_select2, #history_select3").change(function () {
  1556. const leftValue = Number($("#history_select2").val());
  1557. const rightValue = Number($("#history_select3").val());
  1558. drawDiffHistoryBbcode(history_data, leftValue, rightValue, 'torrent', $('#diff_draw'))
  1559. });
  1560.  
  1561. const $historySelect2 = $("#history_select2");
  1562. const $historySelect3 = $("#history_select3");
  1563. // const firstOptionText = $historySelect2.find("option:eq(0)").text();
  1564. const historySelect2OptionsLength = $historySelect2.find("option").length;
  1565.  
  1566. if (historySelect2OptionsLength === 1) {
  1567. // 就一个记录,无法进行差异处理
  1568. $('#diff_draw_unit, #diff_unit').hide();
  1569. } else {
  1570. let rightValue = 0;
  1571. let leftValue = 1;
  1572. let flagBreak = false;
  1573.  
  1574. while (leftValue < historySelect2OptionsLength) {
  1575. if (preCheckBbcodeDiscrepancy(history_data, leftValue, rightValue) === true) {
  1576. $historySelect3.find("option").eq(rightValue).prop("selected", true);
  1577. $historySelect2.find("option").eq(leftValue).prop("selected", true);
  1578. $historySelect2.trigger("change");
  1579. flagBreak = true;
  1580. break;
  1581. }
  1582. rightValue++;
  1583. leftValue++;
  1584. }
  1585.  
  1586. if (!flagBreak) $('#diff_draw_unit, #diff_unit').hide();
  1587.  
  1588. }
  1589.  
  1590. $("#history_select").change(function () { // 监听菜单选择
  1591. let self = Number($(this).val());
  1592. for (let i = 0, len = history_data.length; i < len; i++) {
  1593. if (self !== history_data[i].self) continue;
  1594. history_data[i].banned === 1 ? $('#top').html(history_data[i].title + '&nbsp;&nbsp;&nbsp; <b>[<font class="striking">' + lang['banned'] + '</font>]</b>') : $('#top').text(history_data[i].title);
  1595. // 检查副标题一栏是否存在
  1596. if ($("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").length === 0 && history_data[i].subtitle !== null) {
  1597. $("td[class='rowhead nowrap']:contains(" + lang['uploaded'] + ")").parent().before('<tr><td class="rowhead nowrap" valign="top" align="right">' + lang['subtitle'] + '</td><td class="rowfollow" valign="top" align="left"></td></tr>');
  1598. }
  1599. else if ($("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").length === 1 && history_data[i].subtitle === null) {
  1600. $("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").parent().remove();
  1601. };
  1602. $("td[class='rowhead nowrap']:contains(" + lang['subtitle'] + ")").next().text(history_data[i].subtitle); // 副标题
  1603. $("td[class='rowhead nowrap']:contains(" + lang['description'] + ")").last().next().html('<div id="kdescr"><span style="word-break: break-all; word-wrap: break-word;"><bdo dir="ltr">' + bbcode2html(history_data[i].description_info) + '</bdo></span></div>'); // 描述
  1604. $('#ctdescr').val(history_data[i].description_info); // 描述代码
  1605. $("td[class='rowhead nowrap']:contains(" + lang['basic_info'] + ")").next().html('<b>' + lang['submitted_by'] + '</b>:&nbsp;'
  1606. + ((p) => {
  1607. if (p.uploader_id === null && p.uploader_name === '匿名') return '<i>' + lang['anonymous'] + '</i>'; // 匿名发布
  1608. if (p.uploader_id !== null && p.uploader_name !== '匿名') return '<a href="userdetails.php?id=' + p.uploader_id + '"><b>' + p.uploader_name + '</b></a>'; // 正常显示
  1609. })(history_data[i])
  1610. + '&nbsp;&nbsp;&nbsp;<b>' + lang['submitted_at'] + '</b>:&nbsp;<time>' + history_data[i].uploaded_at.replace('T', ' ')
  1611. + '</time>'
  1612. + (() => { if (history_data[i].torrent_size) { return '&nbsp;&nbsp;&nbsp;<b>' + lang['size'] + ':</b>&nbsp;' + convert(history_data[i].torrent_size) } else { return ''; } })()
  1613. + '&nbsp;&nbsp;&nbsp;<b>' + lang['category'] + '</b>:&nbsp;' + history_data[i].category
  1614. );
  1615. putFileTree(i);
  1616. };
  1617. });
  1618. };
  1619.  
  1620.  
  1621.  
  1622. function bbcode2html(bbcodestr) {
  1623. var tempCode = new Array();
  1624. var tempCodeCount = 0;
  1625. let lost_tags = new Array();
  1626.  
  1627. function addTempCode(value) {
  1628. tempCode[tempCodeCount] = value;
  1629. let returnstr = "<tempCode_" + tempCodeCount + ">";
  1630. tempCodeCount++;
  1631. return returnstr;
  1632. };
  1633.  
  1634. const escape_reg = new RegExp("[&\"\'<>]", "g");
  1635. bbcodestr = bbcodestr.replace(escape_reg, function (s, x) {
  1636. switch (s) {
  1637. case '&':
  1638. return '&amp;';
  1639. case '"':
  1640. return '&quot;';
  1641. case "'":
  1642. return '&#039;';
  1643. case '<':
  1644. return '&lt;';
  1645. case '>':
  1646. return '&gt;';
  1647. default:
  1648. return s;
  1649. };
  1650. });
  1651.  
  1652. bbcodestr = bbcodestr.replace(/\r\n/g, () => { return '<br>' });
  1653. bbcodestr = bbcodestr.replace(/\n/g, () => { return '<br>' });
  1654. bbcodestr = bbcodestr.replace(/\r/g, () => { return '<br>' });
  1655. bbcodestr = bbcodestr.replace(/ /g, ' &nbsp;');
  1656.  
  1657. let br_end = ''; // 对结尾的换行符进行计数
  1658. let br;
  1659. if (br = bbcodestr.match(/(?:<br>)+$/)) {
  1660. br_end = br[0];
  1661. const regex = new RegExp(`${br_end}$`, "");
  1662. bbcodestr = bbcodestr.replace(regex, '');
  1663. };
  1664.  
  1665. const checkLostTags = (value, r_tag_start, r_tag_end) => {
  1666. let state = false;
  1667.  
  1668. let r_tag_start_exec = r_tag_start.exec(value);
  1669. let index_start = r_tag_start_exec ? (r_tag_start_exec.index + r_tag_start_exec[0].length) : 0;
  1670. let r_tag_end_exec = r_tag_end.exec(value.slice(index_start));
  1671.  
  1672. if (r_tag_start_exec && !r_tag_end_exec) {
  1673. let tag_start_val = r_tag_start_exec.groups.tag;;
  1674. console.log('检测到丢失的标签 => ' + `[/${tag_start_val}]`);
  1675. lost_tags.push(`[/${tag_start_val}]`)
  1676. // value = value + `[/${tag_start_val}]`;
  1677. state = true;
  1678. };
  1679.  
  1680. // return { "value": value, "state": state };
  1681. return { "state": state };
  1682. };
  1683.  
  1684. const url = (val, textarea) => {
  1685. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { textarea = textarea.replace(/\[url=.(?:&quot;){0,2}\]/i, function (s) { return '[url]'; }); }
  1686. if (val) {
  1687. const lost = checkLostTags(textarea, /\[(?<tag>url)=[^\[]*?/i, /\[\/(?<tag>url)\]/i);
  1688. if (lost.state) { return textarea.replace(/\[url=[^\[]*?/i, function (s) { return addTempCode(s); }); };
  1689. return textarea.replace(/\[url=(.+?)\](.*?)\[\/url\]/i, function (all, url, text) {
  1690. if (url.match(/\s|\[/)) return addTempCode(all);
  1691. let tmp = url.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1692. if (!tmp.match(/&quot;/)) url = tmp;
  1693. else { if (url.match(/&quot;/g).length === 1) url = url.replace('&quot;', ''); }
  1694. return addTempCode('<a class="faqlink" rel="nofollow noopener noreferer" href="' + url.replace(/&quot;/g, '"') + '">' + text + '</a>');
  1695. });
  1696. } else {
  1697. const lost = checkLostTags(textarea, /\[(?<tag>url)\]/i, /\[\/(?<tag>url)\]/i);
  1698. if (lost.state) { return textarea.replace(/\[url\]/i, function (s) { return addTempCode(s); }); };
  1699. return textarea.replace(/\[url\](.+?)\[\/url\]/i, function (s, x) {
  1700. if (x.match(/\s|\[/i)) return addTempCode(s);
  1701. return addTempCode('<a class="faqlink" rel="nofollow noopener noreferer" href="' + x + '">' + x + '</a>');
  1702. });
  1703. };
  1704. };
  1705.  
  1706. // 注释
  1707. const rt = (val, textarea) => {
  1708. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { return textarea.replace(/\[rt=.*?\]/i, function (s) { return addTempCode(s); }); }
  1709. else if (!val) { return textarea.replace('[rt]', function (s) { return addTempCode(s); }) }
  1710. else {
  1711. const lost = checkLostTags(textarea, /\[(?<tag>rt)=[^\[]*?/i, /\[\/(?<tag>rt)\]/i);
  1712. if (lost.state) { return textarea.replace(/\[rt=[^\[]*?/i, function (s) { return addTempCode(s); }); };
  1713. return textarea.replace(/\[rt=(.+?)\](.*?)\[\/rt\]/i, function (all, tval, text) {
  1714. if (tval.match(/\[/i)) return addTempCode(all);
  1715. let tmp = tval.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1716. if (!tmp.match(/&quot;/)) tval = tmp;
  1717. return addTempCode('<ruby>' + text + '<rp>(</rp><rt>' + tval + '</rt><rp>)</rp></ruby>');
  1718. });
  1719. };
  1720. };
  1721.  
  1722. // 字体
  1723. const font = (val, textarea) => {
  1724. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { return textarea.replace(/\[font=.*?]/i, function (s) { return addTempCode(s); }); }
  1725. else if (!val) { return textarea.replace('[font]', function (s) { return addTempCode(s); }) }
  1726. else {
  1727. const lost = checkLostTags(textarea, /\[(?<tag>font)=[^\[]*?\]/i, /\[\/(?<tag>font)\]/i);
  1728. if (lost.state) { return textarea.replace(/\[font=[^\[]*?/i, function (s) { return addTempCode(s); }); };
  1729. return textarea.replace(/\[font=(.+?)\](.*?)\[\/font\]/i, function (all, tval, text) {
  1730. if (tval.match(/\[/i)) return '[' + addTempCode(`font=`) + `${tval}]${text}`;
  1731. let tmp = tval.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1732. if (!/&quot;/.test(tmp)) { tval = tmp; }
  1733. else { if (tval.match(/&quot;/g).length === 1) tval = tval.replace('&quot;', ''); };
  1734. return '<span style="font-family: ' + tval + '">' + text + '</span>';
  1735. });
  1736. };
  1737. };
  1738.  
  1739. // 颜色
  1740. const color = (val, textarea) => {
  1741. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { return textarea.replace(/\[color=.*?\]/i, function (s) { return addTempCode(s); }); }
  1742. else if (!val) { return textarea.replace('[color]', function (s) { return addTempCode(s); }) }
  1743. else {
  1744. const lost = checkLostTags(textarea, /\[(?<tag>color)=[^\[]*?\]/i, /\[\/(?<tag>color)\]/i);
  1745. if (lost.state) { return textarea.replace(/\[color=[^\[]*?\]/i, function (s) { return addTempCode(s); }); };
  1746. return textarea.replace(/\[color=(.+?)\](.*?)\[\/color\]/i, function (all, tval, text) {
  1747. if (tval.match(/\[/i)) return addTempCode(all);;
  1748. let tmp = tval.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1749. if (!/&quot;/.test(tmp)) { tval = tmp; }
  1750. else { if (tval.match(/&quot;/g).length === 1) tval = tval.replace('&quot;', ''); };
  1751. return '<span style="color: ' + tval + '">' + text + '</span>';
  1752. });
  1753. };
  1754. };
  1755.  
  1756. // 文字大小
  1757. const size = (val, textarea) => {
  1758. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { return textarea.replace(/\[size=.*?\]/i, function (s) { return addTempCode(s); }); }
  1759. else if (!val) { return textarea.replace('[size]', function (s) { return addTempCode(s); }) }
  1760. else {
  1761. const lost = checkLostTags(textarea, /\[(?<tag>size)=[^\[]*?\]/i, /\[\/(?<tag>size)\]/i);
  1762. if (lost.state) { return textarea.replace(/\[size=[^\[]*?\]/i, function (s) { return addTempCode(s); }); };
  1763. return textarea.replace(/\[size=(.+?)\](.*?)\[\/size\]/i, function (all, tval, text) {
  1764. // size只允许1-9的数字
  1765. if (!tval.match(/^(?:&quot;)?[0-9](?:&quot;)?$/)) return addTempCode(all);
  1766. let tmp = tval.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1767. if (!/&quot;/.test(tmp)) { tval = tmp; }
  1768. else { if (tval.match(/&quot;/g).length === 1) tval = tval.replace('&quot;', ''); };
  1769. return '<font size="' + tval + '">' + text + '</font>';
  1770. });
  1771. };
  1772. };
  1773.  
  1774. const pre = (val, textarea) => {
  1775. if (val) { return textarea.replace(/\[pre=(.*?)\]/i, function (s, v) { return addTempCode('[pre=') + v + ']'; }); };
  1776. const lost = checkLostTags(textarea, /\[(?<tag>pre)\]/i, /\[\/(?<tag>pre)\]/i);
  1777. if (lost.state) { return textarea.replace(/\[pre\]/i, function (s) { return addTempCode(s); }); };
  1778. return textarea.replace(/\[pre\](.*?)\[\/pre\]/i, function (all, text) { return '<pre>' + text + '</pre>'; });
  1779. };
  1780.  
  1781. const b = (val, textarea) => {
  1782. if (val) { return textarea.replace(/\[b=(.*?)\]/i, function (s, v) { return addTempCode('[b=') + v + ']'; }); };
  1783. const lost = checkLostTags(textarea, /\[(?<tag>b)\]/i, /\[\/(?<tag>b)\]/i);
  1784. if (lost.state) { return textarea.replace(/\[b\]/i, function (s) { return addTempCode(s); }); };
  1785. return textarea.replace(/\[b\](.*?)\[\/b\]/i, function (all, text) { return '<b>' + text + '</b>'; });
  1786. };
  1787.  
  1788. const i = (val, textarea) => {
  1789. if (val) { return textarea.replace(/\[i=(.*?)\]/i, function (s, v) { return addTempCode('[i=') + v + ']'; }); };
  1790. const lost = checkLostTags(textarea, /\[(?<tag>i)\]/i, /\[\/(?<tag>i)\]/i);
  1791. if (lost.state) { return textarea.replace(/\[i\]/i, function (s) { return addTempCode(s); }); };
  1792. return textarea.replace(/\[i\](.*?)\[\/i\]/i, function (all, text) { return '<em>' + text + '</em>'; });
  1793. };
  1794.  
  1795. const u = (val, textarea) => {
  1796. if (val) { return textarea.replace(/\[u=(.*?)\]/i, function (s, v) { return addTempCode('[u=') + v + ']'; }); };
  1797. const lost = checkLostTags(textarea, /\[(?<tag>u)\]/i, /\[\/(?<tag>u)\]/i);
  1798. if (lost.state) { return textarea.replace(/\[u\]/i, function (s) { return addTempCode(s); }); };
  1799. return textarea.replace(/\[u\](.*?)\[\/u\]/i, function (all, text) { return '<u>' + text + '</u>'; });
  1800. };
  1801.  
  1802. const s = (val, textarea) => {
  1803. if (val) { return textarea.replace(/\[s=(.*?)\]/i, function (s, v) { return addTempCode('[s=') + v + ']'; }); };
  1804. const lost = checkLostTags(textarea, /\[(?<tag>s)\]/i, /\[\/(?<tag>s)\]/i);
  1805. if (lost.state) { return textarea.replace(/\[s\]/i, function (s) { return addTempCode(s); }); };
  1806. return textarea.replace(/\[s\](.*?)\[\/s\]/i, function (all, text) { return '<s>' + text + '</s>'; });
  1807. };
  1808.  
  1809. const img = (val, textarea) => {
  1810. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { return textarea.replace(/\[img=.*?\]/i, function (s) { return addTempCode(s); }); }
  1811. else if (val) {
  1812. return textarea.replace(/\[img=(.*?)\]/i, function (all, url) {
  1813. // [img=http://u2.dmhy.org/pic/logo.png]
  1814. url = url.replace('&amp;', '&');
  1815. if (/^((?!"|'|>|<|;|#).)+\.(?:png|jpg|jpeg|gif|svg|bmp|webp)$/i.test(url)) {
  1816. // url 以 .png 之类结尾
  1817. return addTempCode('<img alt="image" src="' + url + '" style="height: auto; width: auto; max-width: 100%;">');
  1818. } else {
  1819. return addTempCode(all);
  1820. };
  1821. });
  1822. } else {
  1823. // [img]http://u2.dmhy.org/pic/logo.png[/img]
  1824. const lost = checkLostTags(textarea, /\[(?<tag>img)\]/i, /\[\/(?<tag>img)\]/i);
  1825. if (lost.state) { return textarea.replace(/\[img\]/i, function (s) { return addTempCode(s); }); };
  1826. return textarea.replace(/\[img\](.*?)\[\/img\]/i, function (all, url) {
  1827. url = url.replace('&amp;', '&');
  1828. if (/^((?!"|'|>|<|;|#).)+\.(?:png|jpg|jpeg|gif|svg|bmp|webp)$/i.test(url)) {
  1829. // url 以 .png 之类结尾
  1830. return addTempCode('<img alt="image" src="' + url + '" style="height: auto; width: auto; max-width: 100%;">');
  1831. } else {
  1832. return addTempCode(all);
  1833. };
  1834. });
  1835. };
  1836. };
  1837.  
  1838. const imglnk = (val, textarea) => {
  1839. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { return textarea.replace(/\[imglnk=.*?\]/i, function (s) { return addTempCode(s); }); }
  1840. else if (val) {
  1841. return textarea.replace(/\[imglnk=(.*?)\]/i, function (all, url) { return addTempCode('[imglnk=') + url + ']'; });
  1842. } else {
  1843. // [img]http://u2.dmhy.org/pic/logo.png[/img]
  1844. const lost = checkLostTags(textarea, /\[(?<tag>imglnk)\]/i, /\[\/(?<tag>imglnk)\]/i);
  1845. if (lost.state) { return textarea.replace(/\[imglnk\]/i, function (s) { return addTempCode(s); }); };
  1846. return textarea.replace(/\[imglnk\](.*?)\[\/imglnk\]/i, function (all, url) {
  1847. url = url.replace('&amp;', '&');
  1848. if (/^((?!"|'|>|<|;|#).)+\.(?:png|jpg|jpeg|gif|svg|bmp|webp)$/i.test(url)) {
  1849. // url 以 .png 之类结尾
  1850. return addTempCode(`<a class="faqlink" rel="nofollow noopener noreferer" href="' + y + '"><img alt="image" src="${url}" style="height: auto; width: auto; max-width: 100%;"></a>`);
  1851. } else {
  1852. return addTempCode(all);
  1853. };
  1854. });
  1855. };
  1856. };
  1857.  
  1858. const code = (val, textarea) => {
  1859. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { textarea = textarea.replace(/\[code=(?:&quot;){0,2}/, '[code]'); };
  1860. if (val) { textarea = textarea.replace(/\[code=(.*?)\]/i, function (s, v) { return addTempCode('[code=') + v + ']'; }); };
  1861. const lost = checkLostTags(textarea, /\[(?<tag>code)\]/i, /\[\/(?<tag>code)\]/i);
  1862. if (lost.state) { return textarea.replace(/\[code\]/i, function (s) { return addTempCode(s); }); };
  1863. return textarea.replace(/\[code\](.*?)\[\/code\]/i, function (all, text) {
  1864. return addTempCode(`<br><div class="codetop">${lang['code']}</div><div class="codemain">${text.replace(/ &nbsp;/g, ' ')}</div><br />`);
  1865. });
  1866. };
  1867.  
  1868. const info = (val, textarea) => {
  1869. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { textarea = textarea.replace(/\[info=(?:&quot;){0,2}/, '[info]'); };
  1870. if (val) { textarea = textarea.replace(/\[info=(.*?)\]/i, function (s, v) { return addTempCode('[info=') + v + ']'; }); };
  1871. const lost = checkLostTags(textarea, /\[(?<tag>info)\]/i, /\[\/(?<tag>info)\]/i);
  1872. if (lost.state) { return textarea.replace(/\[info\]/i, function (s) { return addTempCode(s); }); };
  1873. return textarea.replace(/\[info\](.*?)\[\/info\]/i, function (all, text) {
  1874. return addTempCode(`<fieldset class="pre"><legend><b><span style="color: blue">${lang['info']}</span></b></legend>${text.replace(/ &nbsp;/g, ' ')}</fieldset>`);
  1875. });
  1876. };
  1877.  
  1878. const mediainfo = (val, textarea) => {
  1879. if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') { textarea = textarea.replace(/\[mediainfo=(?:&quot;){0,2}/, '[mediainfo]'); };
  1880. if (val) { textarea = textarea.replace(/\[mediainfo=(.*?)\]/i, function (s, v) { return addTempCode('[mediainfo=') + v + ']'; }); };
  1881. const lost = checkLostTags(textarea, /\[(?<tag>mediainfo)\]/i, /\[\/(?<tag>mediainfo)\]/i);
  1882. if (lost.state) { return textarea.replace(/\[mediainfo\]/i, function (s) { return addTempCode(s); }); };
  1883. return textarea.replace(/\[mediainfo\](.*?)\[\/mediainfo\]/i, function (all, text) {
  1884. return addTempCode(`<fieldset class="pre"><legend><b><span style="color: red">${lang['mediainfo']}</span></b></legend>${text.replace(/ &nbsp;/g, ' ')}</fieldset>`);
  1885. });
  1886. };
  1887.  
  1888. const quote = (val, textarea) => {
  1889. if (!val) {
  1890. // [quote]我爱U2分享園@動漫花園。[/quote]
  1891. const lost = checkLostTags(textarea, /\[(?<tag>quote)]/i, /\[\/(?<tag>quote)\]/i);
  1892. if (lost.state) { return textarea.replace(/\[quote\]/i, function (s) { return addTempCode(s); }); };
  1893. return textarea.replace(/\[quote\](.*?)\[\/quote\]/i, function (s, x) {
  1894. return '<fieldset><legend>' + lang['quote'] + '</legend>' + x.replace(/(<br>)*$/, '') + '</fieldset>';
  1895. });
  1896. } else if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') {
  1897. // [quote=""]我爱U2分享園@動漫花園。[/quote]
  1898. const lost = checkLostTags(textarea, /\[(?<tag>quote)=[^\[]*?\]/i, /\[\/(?<tag>quote)\]/i);
  1899. if (lost.state) { return textarea.replace(/\[quote=[^\[]*?\]/i, function (s) { return addTempCode(s); }); };
  1900. return textarea.replace(/\[quote=[^\[]*?\](.*?)\[\/quote\]/i, function (s, x) {
  1901. return '<fieldset><legend>' + lang['quote'] + '</legend>' + x.replace(/(<br>)*$/, '') + '</fieldset>';
  1902. });
  1903. } else {
  1904. // [quote="ABC"]我爱U2分享園@動漫花園。[/quote]
  1905. const lost = checkLostTags(textarea, /\[(?<tag>quote)=[^\[]*?\]/i, /\[\/(?<tag>quote)\]/i);
  1906. if (lost.state) { return textarea.replace(/\[quote=[^\[]*?\]/i, function (s) { return addTempCode(s); }); };
  1907. return textarea.replace(/\[quote=([^\[]*?)\](.*?)\[\/quote\]/i, function (all, tval, text) {
  1908. if (tval.match(/\[/i)) return addTempCode(all);;
  1909. let tmp = tval.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1910. if (!/&quot;/.test(tmp)) { tval = tmp; };
  1911. return '<fieldset><legend>' + lang['quote'] + ': ' + tval + '</legend>' + text.replace(/(<br>)*$/, '') + '</fieldset>';
  1912. });
  1913. };
  1914. };
  1915.  
  1916. const spoiler = (val, textarea) => {
  1917. if (!val) {
  1918. // [spoiler]我要剧透了![/spoiler]
  1919. const lost = checkLostTags(textarea, /\[(?<tag>spoiler)]/i, /\[\/(?<tag>spoiler)\]/i);
  1920. if (lost.state) { return textarea.replace(/\[spoiler\]/i, function (s) { return addTempCode(s); }); };
  1921. return textarea.replace(/\[spoiler\](.*?)\[\/spoiler\]/i, function (s, x) {
  1922. return `<table class="spoiler" width="100%"><tbody><tr>`
  1923. + `<td class="colhead">${lang['spoiler']}&nbsp;&nbsp;<button class="spoiler-button-show">${lang['spoiler_button_1']}</button>`
  1924. + `<button class="spoiler-button-hide" style="display: none;">${lang['spoiler_button_2']}</button></td></tr>`
  1925. + `<tr><td><span class="spoiler-content" style="display: none;">${x.replace(/(<br>)*$/, '')}</span></td></tr>`
  1926. + `</tbody></table>`;
  1927. });
  1928. }
  1929. else if (val === '=' || val === '=&quot;' || val === '=&quot;&quot;') {
  1930. // [spoiler=""]真的![/spoiler]
  1931. const lost = checkLostTags(textarea, /\[(?<tag>spoiler)=.+?\]/i, /\[\/(?<tag>spoiler)\]/i);
  1932. if (lost.state) { return textarea.replace(/\[spoiler=[^\[]*?\]/i, function (s) { return addTempCode(s); }); };
  1933. return textarea.replace(/\[spoiler=.*?\](.*?)\[\/spoiler\]/i, function (s, x) {
  1934. return `<table class="spoiler" width="100%"><tbody><tr>`
  1935. + `<td class="colhead">${lang['spoiler']}&nbsp;&nbsp;<button class="spoiler-button-show">${lang['spoiler_button_1']}</button>`
  1936. + `<button class="spoiler-button-hide" style="display: none;">${lang['spoiler_button_2']}</button></td></tr>`
  1937. + `<tr><td><span class="spoiler-content" style="display: none;">${x.replace(/(<br>)*$/, '')}</span></td></tr>`
  1938. + `</tbody></table>`;
  1939. });
  1940. } else {
  1941. // [spoiler="剧透是不可能的!"]真的![/spoiler]
  1942. const lost = checkLostTags(textarea, /\[(?<tag>spoiler)=.+?\]/i, /\[\/(?<tag>spoiler)\]/i);
  1943. if (lost.state) { return textarea.replace(/\[spoiler=[^\[]*?\]/i, function (s) { return addTempCode(s); }); };
  1944. return textarea.replace(/\[spoiler=(.*?)\](.*?)\[\/spoiler\]/i, function (all, tval, text) {
  1945. if (tval.match(/\[/i)) return addTempCode(all);;
  1946. let tmp = tval.replace(/^(?:&quot;)?(.*?)(?:&quot;)?$/, "$1");
  1947. if (!/&quot;/.test(tmp)) tval = tmp;
  1948. return `<table class="spoiler" width="100%"><tbody><tr>`
  1949. + `<td class="colhead">${tval}&nbsp;&nbsp;<button class="spoiler-button-show">${lang['spoiler_button_1']}</button>`
  1950. + `<button class="spoiler-button-hide" style="display: none;">${lang['spoiler_button_2']}</button></td></tr>`
  1951. + `<tr><td><span class="spoiler-content" style="display: none;">${text.replace(/(<br>)*$/, '')}</span></td></tr>`
  1952. + `</tbody></table>`;
  1953. });
  1954. };
  1955. };
  1956.  
  1957. const localConvert = (textarea) => {
  1958. let convert_count = 0;
  1959. let index = 0;
  1960. let _textarea = textarea;
  1961. let bbcode_tag;
  1962. while (bbcode_tag = /\[(?<tag>b|i|u|s|color|size|font|rt|mediainfo|info|code|url|img|imglnk|quote|pre|spoiler)(?<val>=[^\[]*?)?\]/gi.exec(_textarea)) {
  1963. let t;
  1964. let tag = bbcode_tag.groups.tag;
  1965. let val = bbcode_tag.groups.val;
  1966. index = bbcode_tag.index;
  1967. _textarea = _textarea.slice(index);
  1968. // console.log(_textarea);
  1969. // console.log(`当前标签:` + tag + ' | ' + val);
  1970. switch (tag.toLowerCase()) {
  1971. case 'b':
  1972. t = b(val, _textarea); break;
  1973. case 'i':
  1974. t = i(val, _textarea); break;
  1975. case 'u':
  1976. t = u(val, _textarea); break;
  1977. case 's':
  1978. t = s(val, _textarea); break;
  1979. case 'color':
  1980. t = color(val, _textarea); break;
  1981. case 'size':
  1982. t = size(val, _textarea); break;
  1983. case 'font':
  1984. t = font(val, _textarea); break;
  1985. case 'rt':
  1986. t = rt(val, _textarea); break;
  1987. case 'mediainfo':
  1988. t = mediainfo(val, _textarea); break;
  1989. case 'info':
  1990. t = info(val, _textarea); break;
  1991. case 'code':
  1992. t = code(val, _textarea); break;
  1993. case 'url':
  1994. t = url(val, _textarea); break;
  1995. case 'img':
  1996. t = img(val, _textarea); break;
  1997. case 'imglnk':
  1998. t = imglnk(val, _textarea); break;
  1999. case 'quote':
  2000. t = quote(val, _textarea); break;
  2001. case 'pre':
  2002. t = pre(val, _textarea); break;
  2003. case 'spoiler':
  2004. t = spoiler(val, _textarea); break;
  2005. default:
  2006. break;;
  2007. };
  2008. textarea = textarea.replace(_textarea, t);
  2009. _textarea = t;
  2010. if (++convert_count > 5000) break;
  2011. // console.log('发生次数: ' + convert_count);
  2012. // console.log(textarea);
  2013. };
  2014. return textarea;
  2015. };
  2016.  
  2017. bbcodestr = localConvert(bbcodestr);
  2018.  
  2019. // 没有bbcode包裹的超链接
  2020. bbcodestr = bbcodestr.replace(/((?:https?|ftp|gopher|news|telnet|mms|rtsp):\/\/((?!&lt;|&gt;|\s|"|>|'|<|\(|\)|\[|\]).)+)/gi, function (s, x) {
  2021. return '<a class="faqlink" rel="nofollow noopener noreferer" href="' + s + '">' + s + '</a>';
  2022. });
  2023.  
  2024. // 单个标签 不带参
  2025. const o_reg = new RegExp("\\[(\\*|siteurl|site)\\]", "gi");
  2026. bbcodestr = bbcodestr.replace(o_reg, function (s, x, y) {
  2027. switch (x) {
  2028. case '*':
  2029. return '<img class="listicon listitem" src="pic/trans.gif" alt="list">';
  2030. case 'site':
  2031. return 'U2分享園@動漫花園';
  2032. case 'siteurl':
  2033. return 'https://u2.dmhy.org';
  2034. default:
  2035. return s;
  2036. };
  2037. });
  2038.  
  2039. // 表情
  2040. const em_reg = new RegExp("\\[(em[1-9][0-9]*)\\]", "gi");
  2041. bbcodestr = bbcodestr.replace(em_reg, function (s, x) {
  2042. switch (x) {
  2043. case (x.match(/^em[1-9][0-9]*/i) || {}).input:
  2044. return '<img src="pic/smilies/' + x.replace("em", "") + '.gif" alt="[' + x + ']">';
  2045. default:
  2046. return s;
  2047. };
  2048. });
  2049.  
  2050.  
  2051. for (let i = 0, len = tempCode.length; i < len; i++) {
  2052. bbcodestr = bbcodestr.replace("<tempCode_" + i + ">", tempCode[i]);
  2053. };
  2054.  
  2055. bbcodestr = bbcodestr + br_end;
  2056. if (/(<br>)$/.test(bbcodestr)) { bbcodestr = bbcodestr + '<br>' };
  2057.  
  2058. // lost_tags
  2059. // if (lost_tags.length !== 0) {
  2060. // $('#preview_bbcode').html(`⚠ ${lang['preview']}`);
  2061. // $('#preview_bbcode').attr('title', [...new Set(lost_tags)].join('\n'))
  2062. // } else {
  2063. // $('#preview_bbcode').html(lang['preview']);
  2064. // $('#preview_bbcode').attr('title', '')
  2065. // };
  2066.  
  2067. let htmlobj = document.createElement('div');
  2068. htmlobj.innerHTML = bbcodestr;
  2069.  
  2070. $(htmlobj).children('fieldset').children('fieldset').children('fieldset').children('fieldset').each(function () {
  2071. $(this).html($(this).html().replace(/(^<legend>[^<]*?<\/legend>)(.*)/i, function (s, x, y) {
  2072. return x + '<table class="spoiler" width="100%"><tbody>'
  2073. + '<tr><td class="colhead">' + lang['auto_fold'] + '&nbsp;&nbsp;'
  2074. + '<button class="spoiler-button-show">' + lang['spoiler_button_1'] + '</button>'
  2075. + '<button class="spoiler-button-hide" style="display: none;">' + lang['spoiler_button_2'] + '</button>'
  2076. + '</td></tr><tr><td><span class="spoiler-content" style="display: none;">'
  2077. + y + '</span></td></tr></tbody></table>';
  2078. }));
  2079. });
  2080.  
  2081. return $(htmlobj).html();
  2082. };
  2083.  
  2084.  
  2085. function getapi() {
  2086. return new Promise((resolve, reject) => {
  2087. // https://www.w3school.com.cn/jquery/ajax_ajax.asp
  2088. $.ajax({
  2089. type: 'get',
  2090. url: 'https://u2.kysdm.com/api/v1/history?token=' + token + '&maximum=50&uid=' + user_id + '&torrent_id=' + torrent_id,
  2091. contentType: 'application/json',
  2092. dataType: 'json',
  2093. cache: true,
  2094. success: r => resolve(r),
  2095. error: r => {
  2096. console.log('发生错误,HTTP状态码[' + r.status + ']。');
  2097. reject(r.status)
  2098. },
  2099. });
  2100. }).catch(() => { return { "state": "404", "msg": "failure", "data": { "history": [] } }; });
  2101. };
  2102.  
  2103.  
  2104. function lang_init(lang) {
  2105. const lang_json = {
  2106. "zh_CN": {
  2107. "quote": "引用",
  2108. "info": "发布信息",
  2109. "mediainfo": "媒体信息",
  2110. "code": "代码",
  2111. "spoiler": "警告!下列文字很可能泄露剧情,请谨慎选择是否观看。",
  2112. "spoiler_button_1": "我就是手贱",
  2113. "spoiler_button_2": "我真是手贱",
  2114. "main_title": "主标题",
  2115. "rt_text": "请输入上标",
  2116. "main_body": "请输入正文",
  2117. "main_body_prefix": "请输入标题",
  2118. "url_name": "请输入网址名称",
  2119. "url_link": "请输入网址链接",
  2120. "select_type": "请选择分类...",
  2121. "preview": "预览",
  2122. "auto_fold": "过深引用自动折叠",
  2123. "subtitle": "副标题",
  2124. "uploaded": "发布人",
  2125. "basic_info": "基本信息",
  2126. "description": "描述",
  2127. "history_select_loading": "正在努力加载中...",
  2128. "history_select_error": "加载失败啦 (ノДT)",
  2129. "anonymous": "匿名",
  2130. "uploaded_at": "发布时间",
  2131. "size": "大小",
  2132. "category": "类型",
  2133. "submitted_by": "提供者",
  2134. "submitted_at": "提交时间",
  2135. "history_text_loading": "~~正在检查历史数据中~~",
  2136. "history_text_error": "加载失败啦 (ノДT) %%",
  2137. "history_text_empty": "半条历史记录都没有 (ノДT) @@",
  2138. "torrent_title": "种子标题",
  2139. "torrent_info": "种子信息",
  2140. "torrent_ver": "种子版本",
  2141. "torrent_piece_length": "种子区块",
  2142. "files": "文件数",
  2143. "info_hash": "种子散列值",
  2144. "show_or_hide": "显示&nbsp;/&nbsp;隐藏",
  2145. "KiB": " KiB",
  2146. "MiB": " MiB",
  2147. "GiB": " GiB",
  2148. "TiB": " TiB",
  2149. "current_time": " 当前时间",
  2150. "anonymous_user": " 匿名用户",
  2151. "system": " 系统",
  2152. "banned": "已屏蔽",
  2153. "google_backup": "谷歌备份",
  2154. "google_send": "发送请求",
  2155. "last_edited": "最后编辑",
  2156. "back_to_top": "返回顶部",
  2157. "reset_token": "重置Token (・_・)ヾ",
  2158. },
  2159. "zh_TW": {
  2160. "quote": "引用",
  2161. "info": "發佈訊息",
  2162. "mediainfo": "媒體訊息",
  2163. "code": "代碼",
  2164. "spoiler": "警告!下列文字很可能洩露劇情,請謹慎選擇是否觀看。",
  2165. "spoiler_button_1": "我就是手賤",
  2166. "spoiler_button_2": "我真是手賤",
  2167. "main_title": "主標題",
  2168. "rt_text": "請輸入上標",
  2169. "main_body": "請輸入正文",
  2170. "main_body_prefix": "請輸入標題",
  2171. "url_name": "請輸入網址名稱",
  2172. "url_link": "請輸入網址連結",
  2173. "select_type": "請選擇分類...",
  2174. "preview": "預覽",
  2175. "auto_fold": "過深引用自動摺疊",
  2176. "subtitle": "副標題",
  2177. "uploaded": "發布人",
  2178. "basic_info": "基本訊息",
  2179. "description": "描述",
  2180. "history_select_loading": "正在努力載入中...",
  2181. "history_select_error": "載入失敗啦 (ノДT)",
  2182. "anonymous": "匿名",
  2183. "uploaded_at": "發布時間",
  2184. "size": "大小",
  2185. "category": "類型",
  2186. "submitted_by": "提供者",
  2187. "submitted_at": "提交時間",
  2188. "history_text_loading": "~~正在檢查歷史數據中~~",
  2189. "history_text_error": "載入失敗啦 (ノДT) %%",
  2190. "history_text_empty": "半條歷史記錄都沒有 (ノДT) @@",
  2191. "torrent_title": "種子標題",
  2192. "torrent_info": "種子訊息",
  2193. "torrent_ver": "種子版本",
  2194. "torrent_piece_length": "種子區塊",
  2195. "files": "文件數",
  2196. "info_hash": "種子散列值",
  2197. "show_or_hide": "顯示&nbsp;/&nbsp;隱藏",
  2198. "KiB": " KiB",
  2199. "MiB": " MiB",
  2200. "GiB": " GiB",
  2201. "TiB": " TiB",
  2202. "current_time": " 當前時間",
  2203. "anonymous_user": " 匿名用戶",
  2204. "system": " 系統",
  2205. "banned": "已屏蔽",
  2206. "google_backup": "Google備份",
  2207. "google_send": "發送請求",
  2208. "last_edited": "最後編輯",
  2209. "back_to_top": "返回頂部",
  2210. "reset_token": "重設Token (・_・)ヾ",
  2211. },
  2212. "zh_HK": {
  2213. "quote": "引用",
  2214. "info": "發佈訊息",
  2215. "mediainfo": "媒體訊息",
  2216. "code": "代碼",
  2217. "spoiler": "警告!下列文字很可能洩露劇情,請謹慎選擇是否觀看。",
  2218. "spoiler_button_1": "我就是手賤",
  2219. "spoiler_button_2": "我真是手賤",
  2220. "main_title": "主標題",
  2221. "rt_text": "請輸入上標",
  2222. "main_body": "請輸入正文",
  2223. "main_body_prefix": "請輸入標題",
  2224. "url_name": "請輸入網址名稱",
  2225. "url_link": "請輸入網址鏈接",
  2226. "select_type": "請選擇分類...",
  2227. "preview": "預覽",
  2228. "auto_fold": "過深引用自動摺疊",
  2229. "subtitle": "副標題",
  2230. "uploaded": "發布人",
  2231. "basic_info": "基本訊息",
  2232. "description": "描述",
  2233. "history_select_loading": "正在努力加載中...",
  2234. "history_select_error": "加載失敗啦 (ノДT)",
  2235. "anonymous": "匿名",
  2236. "uploaded_at": "發佈時間",
  2237. "size": "大小",
  2238. "category": "類型",
  2239. "submitted_by": "提供者",
  2240. "submitted_at": "提交時間",
  2241. "history_text_loading": "~~正在檢查歷史數據中~~",
  2242. "history_text_error": "加載失敗啦 (ノДT) %%",
  2243. "history_text_empty": "半條歷史記錄都沒有 (ノДT) @@",
  2244. "torrent_title": "種子標題",
  2245. "torrent_info": "種子訊息",
  2246. "torrent_ver": "種子版本",
  2247. "torrent_piece_length": "種子區塊",
  2248. "files": "文件數",
  2249. "info_hash": "種子散列值",
  2250. "show_or_hide": "顯示&nbsp;/&nbsp;隱藏",
  2251. "KiB": " KiB",
  2252. "MiB": " MiB",
  2253. "GiB": " GiB",
  2254. "TiB": " TiB",
  2255. "current_time": " 當前時間",
  2256. "anonymous_user": " 匿名用戶",
  2257. "system": " 系統",
  2258. "banned": "已屏蔽",
  2259. "google_backup": "Google備份",
  2260. "google_send": "發送請求",
  2261. "last_edited": "最後編輯",
  2262. "back_to_top": "返回頂部",
  2263. "reset_token": "重置Token (・_・)ヾ",
  2264. },
  2265. "en_US": {
  2266. "quote": "Quote",
  2267. "info": "Infobox",
  2268. "mediainfo": "Media Info",
  2269. "code": "CODE",
  2270. "spoiler": "Warning! This section contains spoiler!",
  2271. "spoiler_button_1": "I agree to view this.",
  2272. "spoiler_button_2": "Hide this.",
  2273. "main_title": "Main Title",
  2274. "rt_text": "Please enter superscript",
  2275. "main_body": "Please enter the text",
  2276. "main_body_prefix": "Please enter a title",
  2277. "url_name": "Please enter the URL name",
  2278. "url_link": "Please enter the URL link",
  2279. "select_type": "Please select a type.",
  2280. "preview": "Preview",
  2281. "auto_fold": "Over quote auto fold",
  2282. "subtitle": "Small Description",
  2283. "uploaded": "Uploader",
  2284. "basic_info": "Basic Info",
  2285. "description": "Description",
  2286. "history_select_loading": "Trying to load now ...",
  2287. "history_select_error": "Load failure (ノДT)",
  2288. "anonymous": "Anonymous",
  2289. "uploaded_at": "Uploaded at",
  2290. "size": "Size",
  2291. "category": "Category",
  2292. "submitted_by": "Submitted by",
  2293. "submitted_at": "Submitted at",
  2294. "history_text_loading": "~~Checking historical data now~~",
  2295. "history_text_error": "Load failure (ノДT) %%",
  2296. "history_text_empty": "Half of the history is missing (ノДT) @@",
  2297. "torrent_title": "Torrent Title",
  2298. "torrent_info": "Torrent Info",
  2299. "torrent_ver": "Torrent Ver",
  2300. "torrent_piece_length": "Torrent Piece Length",
  2301. "files": "Files",
  2302. "info_hash": "Info hash",
  2303. "show_or_hide": "Show&nbsp;or&nbsp;Hide",
  2304. "KiB": " KiB",
  2305. "MiB": " MiB",
  2306. "GiB": " GiB",
  2307. "TiB": " TiB",
  2308. "current_time": " CurrentTime",
  2309. "anonymous_user": " AnonymousUser",
  2310. "system": " 系統",
  2311. "banned": "Banned",
  2312. "google_backup": "Google Backup",
  2313. "google_send": "Send request",
  2314. "last_edited": "Last edited by",
  2315. "back_to_top": "Back to top",
  2316. "reset_token": "Reset Token (・_・)ヾ",
  2317. },
  2318. "ru_RU": {
  2319. "quote": "Цитата",
  2320. "info": "Отправленные",
  2321. "mediainfo": "Данные о Медиа",
  2322. "code": "CODE",
  2323. "spoiler": "Предупреждение! Данный раздел содержит СПОЙЛЕРЫ!",
  2324. "spoiler_button_1": "I agree to view this.",
  2325. "spoiler_button_2": "Hide this.",
  2326. "main_title": "Основное название",
  2327. "rt_text": "Пожалуйста, введите надстрочный индекс",
  2328. "main_body": "Пожалуйста, введите текст",
  2329. "main_body_prefix": "Пожалуйста, введите название",
  2330. "url_name": "Пожалуйста, введите имя URL",
  2331. "url_link": "Пожалуйста, введите URL-ссылку",
  2332. "select_type": "выберите тип ...",
  2333. "preview": "Предварительный просмотр",
  2334. "auto_fold": "Автоматическое складывание для более глубоких ссылок",
  2335. "subtitle": "Краткое Описание",
  2336. "uploaded": "Загрузил",
  2337. "basic_info": "Базовая инф.",
  2338. "description": "Описание",
  2339. "history_select_loading": "Пытаюсь загрузить сейчас ...",
  2340. "history_select_error": "Отказ нагрузки (ノДT)",
  2341. "anonymous": "Анонимно",
  2342. "uploaded_at": "Загружен",
  2343. "size": "Размер",
  2344. "category": "Категория",
  2345. "submitted_by": "Разместивший Запрос",
  2346. "submitted_at": "Дата размещения",
  2347. "history_text_loading": "~~Проверка исторических данных сейчас~~",
  2348. "history_text_error": "Отказ нагрузки (ノДT) %%",
  2349. "history_text_empty": "Половина истории отсутствует (ノДT) @@",
  2350. "torrent_title": "Имя торрента",
  2351. "torrent_info": "Информация о торренте",
  2352. "torrent_ver": "Торрент Вер",
  2353. "torrent_piece_length": "Длина куска торрента",
  2354. "files": "Файлов в торренте",
  2355. "info_hash": "Информация о ХЕШЕ",
  2356. "show_or_hide": "Показать&nbsp;/&nbsp;Скрыть",
  2357. "KiB": " KiБ",
  2358. "MiB": " MiБ",
  2359. "GiB": " GiБ",
  2360. "TiB": " TiБ",
  2361. "current_time": " Текущее время",
  2362. "anonymous_user": " Анонимный пользователь",
  2363. "system": " система",
  2364. "banned": "Забанен",
  2365. "google_backup": "Резервное копирование Google",
  2366. "google_send": "послать запрос",
  2367. "last_edited": "Последний раз редактировалось",
  2368. "back_to_top": "На главную",
  2369. "reset_token": "Токен сброса (・_・)ヾ",
  2370. }
  2371. };
  2372. return lang_json[lang];
  2373. };
  2374.  
  2375. // 当前时间 字符串格式
  2376. function getDateString() {
  2377. function zero(obj) { return obj < 10 ? '0' + obj : obj };
  2378. const time = new Date();
  2379. return time.getFullYear().toString() + '-' + zero(time.getMonth() + 1).toString() + '-' + zero(time.getDate()).toString()
  2380. + ' ' + zero(time.getHours()) + ':' + zero(time.getMinutes()) + ':' + zero(time.getSeconds())
  2381. };
  2382.  
  2383. function convert(s) {
  2384. if (s / 1024 < 1024) return (s / 1024).toFixed(3) + lang['KiB']
  2385. if (s / 1024 / 1024 < 1024) return (s / 1024 / 1024).toFixed(3) + lang['MiB']
  2386. if (s / 1024 / 1024 / 1024 < 1024) return (s / 1024 / 1024 / 1024).toFixed(3) + lang['GiB']
  2387. if (s / 1024 / 1024 / 1024 / 1024 < 1024) return (s / 1024 / 1024 / 1024 / 1024).toFixed(3) + lang['TiB']
  2388. };
  2389.  
  2390. // 生成种子评论cid定位url
  2391. const cidUrl = (t, c) => {
  2392. if (/\/offers\.php/i.test(location.href)) {
  2393. return `offers.php?id=${t}&off_details=1#cid${c}`
  2394. } else if (/\/details\.php/i.test(location.href))
  2395. return `details.php?id=${t}#cid${c}`
  2396. else {
  2397. return '/'
  2398. };
  2399. };
  2400.  
  2401. // 对JSON进行排序
  2402. // https://github.com/substack/json-stable-stringify
  2403. const stringify = function (obj, opts) {
  2404. if (!opts) opts = {};
  2405. if (typeof opts === 'function') opts = { cmp: opts };
  2406. var space = opts.space || '';
  2407. if (typeof space === 'number') space = Array(space + 1).join(' ');
  2408. var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
  2409. var replacer = opts.replacer || function (key, value) { return value; };
  2410.  
  2411. var cmp = opts.cmp && (function (f) {
  2412. return function (node) {
  2413. return function (a, b) {
  2414. var aobj = { key: a, value: node[a] };
  2415. var bobj = { key: b, value: node[b] };
  2416. return f(aobj, bobj);
  2417. };
  2418. };
  2419. })(opts.cmp);
  2420.  
  2421. var seen = [];
  2422. return (function stringify(parent, key, node, level) {
  2423. var indent = space ? ('\n' + new Array(level + 1).join(space)) : '';
  2424. var colonSeparator = space ? ': ' : ':';
  2425.  
  2426. if (node && node.toJSON && typeof node.toJSON === 'function') {
  2427. node = node.toJSON();
  2428. }
  2429.  
  2430. node = replacer.call(parent, key, node);
  2431.  
  2432. if (node === undefined) {
  2433. return;
  2434. }
  2435. if (typeof node !== 'object' || node === null) {
  2436. return JSON.stringify(node);
  2437. }
  2438. if (isArray(node)) {
  2439. var out = [];
  2440. for (var i = 0; i < node.length; i++) {
  2441. var item = stringify(node, i, node[i], level + 1) || JSON.stringify(null);
  2442. out.push(indent + space + item);
  2443. }
  2444. return '[' + out.join(',') + indent + ']';
  2445. }
  2446. else {
  2447. if (seen.indexOf(node) !== -1) {
  2448. if (cycles) return JSON.stringify('__cycle__');
  2449. throw new TypeError('Converting circular structure to JSON');
  2450. }
  2451. else seen.push(node);
  2452.  
  2453. var keys = objectKeys(node).sort(cmp && cmp(node));
  2454. var out = [];
  2455. for (var i = 0; i < keys.length; i++) {
  2456. var key = keys[i];
  2457. var value = stringify(node, key, node[key], level + 1);
  2458.  
  2459. if (!value) continue;
  2460.  
  2461. var keyValue = JSON.stringify(key)
  2462. + colonSeparator
  2463. + value;
  2464. ;
  2465. out.push(indent + space + keyValue);
  2466. }
  2467. seen.splice(seen.indexOf(node), 1);
  2468. return '{' + out.join(',') + indent + '}';
  2469. }
  2470. })({ '': obj }, '', obj, 0);
  2471. };
  2472.  
  2473. const isArray = Array.isArray || function (x) {
  2474. return {}.toString.call(x) === '[object Array]';
  2475. };
  2476.  
  2477. const objectKeys = Object.keys || function (obj) {
  2478. var has = Object.prototype.hasOwnProperty || function () { return true };
  2479. var keys = [];
  2480. for (var key in obj) {
  2481. if (has.call(obj, key)) keys.push(key);
  2482. }
  2483. return keys;
  2484. };
  2485.  
  2486. function convertBytesToAutoUnit(bytes) {
  2487. var units = ['B', 'KiB', 'MiB'];
  2488. var unitIndex = 0;
  2489.  
  2490. while (bytes >= 1024 && unitIndex < units.length - 1) {
  2491. bytes >>= 10;
  2492. unitIndex++;
  2493. };
  2494.  
  2495. return bytes + units[unitIndex];
  2496. };
  2497.  
  2498. async function loadExternalCssAsync(url) {
  2499. return new Promise((resolve) => {
  2500. const link = document.createElement("link");
  2501. link.rel = "stylesheet";
  2502. link.type = "text/css";
  2503. link.href = url;
  2504. link.onload = resolve;
  2505. link.onerror = resolve;
  2506. document.head.appendChild(link);
  2507. });
  2508. }
  2509.  
  2510. function addGlobalStyles(cssRules) {
  2511. const styleElement = document.createElement('style');
  2512. styleElement.appendChild(document.createTextNode(cssRules));
  2513. document.head.appendChild(styleElement);
  2514. }
  2515.  
  2516. function diffHistoryCommentBbcode(comments, type) {
  2517. const elementId = type === 'torrent' ? '[id^=cid]' : '[id^=pid]';
  2518.  
  2519. $('.diff_comment_button_close').click(function () {
  2520. $(this).hide();
  2521. $(this).next().show();
  2522.  
  2523. type === 'torrent' ? $(this).parents(elementId).parent().next().find('[class="rowfollow"]:last').show() : $(this).parents(elementId).parent().next().find('.post-body').parent().show();
  2524.  
  2525. $(this).parents(elementId).parent().next().find('.diff_comment_body').hide();
  2526. })
  2527.  
  2528. $('.diff_comment_button_open').click(function () {
  2529. $(this).hide();
  2530. $(this).prev().show();
  2531.  
  2532. const options = $(this).next().html();
  2533. const post = type === 'torrent' ? $(this).parents(elementId).parent().next().find('[class="rowfollow"]:last') : $(this).parents(elementId).parent().next().find('.post-body').parent(); // 文字部分
  2534.  
  2535. post.hide()
  2536.  
  2537. if ($(this).parents(elementId).parent().next().find('.draw-div').length !== 0) {
  2538. // 已插入过的
  2539. $(this).parents(elementId).parent().next().find('.diff_comment_body').show();
  2540. return;
  2541. }
  2542.  
  2543. post.after(`<td class="diff_comment_body" valign="top">
  2544. <table style="width: 100%; height: 100%; border-collapse: collapse; border: none; background-color: transparent;">
  2545. <tr>
  2546. <td valign="top" style="border: none;">
  2547. <div class="diff-container" style="">
  2548. <div class="diff-cell">&nbsp;<select name="type" class="diff_comment_left_select">${options}</select></div>
  2549. <div class="diff-cell"><select name="type" class="diff_comment_right_select">${options}</select></div>
  2550. </div>
  2551. </td>
  2552. </tr>
  2553. <tr>
  2554. <td valign="top" style="border: none;">
  2555. <div class="draw-div"></div>
  2556. </td>
  2557. </tr>
  2558. </table>
  2559. </td>`);
  2560.  
  2561. const leftSelect = $(this).parents(elementId).parent().next().find('.diff_comment_left_select');
  2562. const rightSelect = $(this).parents(elementId).parent().next().find('.diff_comment_right_select');
  2563. const drawElement = $(this).parents(elementId).parent().next().find('.draw-div');
  2564.  
  2565. leftSelect.add(rightSelect).change(function () {
  2566. const leftValue = Number(leftSelect.val());
  2567. const rightValue = Number(rightSelect.val());
  2568. console.log(`${leftValue} | ${rightValue}`);
  2569. drawDiffHistoryBbcode(comments, leftValue, rightValue, 'comment', drawElement);
  2570. });
  2571.  
  2572. let leftEq = 1;
  2573. let rightValue = Number(rightSelect.find('option:eq(0)').val());
  2574. var optionCount = rightSelect.find('option').length;
  2575. let leftBbcode, rightBbcode;
  2576. let flagBreak = false;
  2577.  
  2578. while (leftEq < optionCount) {
  2579. let leftValue = Number(leftSelect.find(`option:eq(${leftEq})`).val());
  2580.  
  2581. for (let i = 0, len = comments.length; i < len; i++) {
  2582. if (comments[i].self === leftValue) {
  2583. leftBbcode = comments[i].bbcode;
  2584. } else if (comments[i].self === rightValue) {
  2585. rightBbcode = comments[i].bbcode;
  2586. }
  2587.  
  2588. if (leftBbcode !== undefined && leftBbcode !== "" && rightBbcode !== undefined && rightBbcode !== "") break;
  2589.  
  2590. }
  2591.  
  2592. if (leftBbcode !== rightBbcode) {
  2593. leftSelect.find(`option:eq(${leftEq})`).prop("selected", true);
  2594. leftSelect.trigger("change");
  2595. flagBreak = true;
  2596. break;
  2597. }
  2598. leftBbcode = '';
  2599. leftEq++;
  2600. }
  2601.  
  2602. if (!flagBreak) {
  2603. // 如果修改记录的BBCODE都一样
  2604. leftSelect.find(`option:eq(1)`).prop("selected", true);
  2605. rightSelect.find(`option:eq(0)`).prop("selected", true);
  2606. leftSelect.trigger("change");
  2607. }
  2608.  
  2609. });
  2610.  
  2611. }
  2612.  
  2613. function preCheckBbcodeDiscrepancy(data, left, right) {
  2614. // 预检测两边BBCODE是否存在差异
  2615. let leftBbcode = generateBbcode(data[left]);
  2616. let rightBbcode = generateBbcode(data[right]);
  2617. return !(leftBbcode === rightBbcode || typeof leftBbcode === 'undefined' || typeof rightBbcode === 'undefined');
  2618. }
  2619.  
  2620. function generateBbcode(data) {
  2621. return `Title: ${data.title}
  2622.  
  2623. Subtitle: ${data.subtitle || 'N/A'}
  2624.  
  2625. Uploader Name: ${data.uploader_name}
  2626.  
  2627. Category: ${data.category}
  2628.  
  2629. Anidb: ${data.anidb || 'N/A'} *此变动不精准,仅供参考。
  2630.  
  2631. Description:
  2632. ${data.description_info}`;
  2633. }
  2634.  
  2635. function drawDiffHistoryBbcode(data, leftValue, rightValue, type, drawElement) {
  2636. /* addGlobalStyles(`.diff-container{
  2637. display: flex;
  2638. align-items: flex-start;
  2639. justify-content: flex-start;
  2640. }
  2641. .diff-cell{
  2642. border: none;
  2643. padding: 0;
  2644. margin-left: 5px;
  2645. flex: 1;
  2646. }
  2647. .draw-div{
  2648. box-sizing: border-box;
  2649. max-width: 100%;
  2650. min-height: 15px;
  2651. max-height: 600px;
  2652. margin: 5px;
  2653. overflow: auto;
  2654. border-top: 1px solid #bfbfbf;
  2655. border-bottom: 1px solid #bfbfbf;
  2656. }
  2657. .diff-table {
  2658. width: 100%;
  2659. border-left: 1px solid #bfbfbf;
  2660. border-right: 1px solid #bfbfbf;
  2661. background-color: white;
  2662. }
  2663. .diff-table table, .diff-table table td{
  2664. background-color: transparent;
  2665. border: none;
  2666. vertical-align: top;
  2667. }
  2668. .diff-table, .diff-table table {
  2669. border-collapse: collapse;
  2670. box-sizing: border-box;
  2671. table-layout: fixed;
  2672. font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
  2673. font-size: 12px;
  2674. }
  2675. .diff-table tbody {
  2676. vertical-align: top;
  2677. }
  2678. .diff-table del {
  2679. text-decoration: none;
  2680. background-color: #ff818266;
  2681. }
  2682. .diff-table ins {
  2683. text-decoration: none;
  2684. background-color: #abf2bc;
  2685. }
  2686. .diff-linenumber {
  2687. text-align: right;
  2688. vertical-align: top;
  2689. width: 3em;
  2690. border: none;
  2691. color: #6e7781;
  2692. font-size: 12px;
  2693. }
  2694. .diff-linenumber-delete {
  2695. background-color: #ffd7d5;
  2696. }
  2697. .diff-linenumber-insert {
  2698. background-color: #ccffd8;
  2699. }
  2700. .diff-line-text-delete {
  2701. background-color: #ffebe9;
  2702. }
  2703. .diff-line-text-insert {
  2704. background-color: #e6ffec;
  2705. }
  2706. .diff-linenumber-empty, .diff-text-cell-empty {
  2707. background-color: #d0d8e080;
  2708. }
  2709. .diff-line-text {
  2710. display: inline-block;
  2711. white-space: pre-wrap;
  2712. overflow-wrap: break-word;
  2713. word-break: break-word;
  2714. box-sizing: border-box;
  2715. width: auto;
  2716. font-size: 12px;
  2717. }
  2718. .diff-line-prefix {
  2719. background: none;
  2720. word-wrap: break-word;
  2721. display: inline;
  2722. font-size: 12px;
  2723. box-sizing: border-box;
  2724. vertical-align: top;
  2725. }
  2726. .diff-line-prefix-delete::before {
  2727. content: " - ";
  2728. }
  2729. .diff-line-prefix-insert::before {
  2730. content: " + ";
  2731. }
  2732. .diff-line-prefix-empty::before {
  2733. content: " ";
  2734. }
  2735. .diff-text-cell, .diff-text-cell-empty {
  2736. width: auto;
  2737. white-space: pre;
  2738. border-left: none;
  2739. border-right: 1px solid #bfbfbf;
  2740. border-top: none;
  2741. border-bottom: none;
  2742. }`); */
  2743.  
  2744. // 生成BBCODE内容
  2745. for (let i = 0, len = data.length; i < len; i++) {
  2746. if (data[i].self === leftValue) {
  2747. var leftBbcode = type === 'torrent' ? generateBbcode(data[i]) : data[i].bbcode;
  2748. } else if (data[i].self === rightValue) {
  2749. var rightBbcode = type === 'torrent' ? generateBbcode(data[i]) : data[i].bbcode;
  2750. }
  2751. }
  2752.  
  2753. if (leftBbcode === rightBbcode || typeof leftBbcode === 'undefined' || typeof rightBbcode === 'undefined') {
  2754. drawElement.html(`<table class="diff-table">
  2755. <tbody id="diff-tbody">
  2756. <tr style="background-color: #ddf4ff;">
  2757. <td class="diff-linenumber">
  2758. </td>
  2759. <td class="diff-text-cell" style="border-right: none; color: #6e7781;">
  2760. <span>bbcode without changes</span>
  2761. </td>
  2762. <td class="diff-linenumber"></td>
  2763. <td class="diff-text-cell"></td>
  2764. </tr>
  2765. </tbody>
  2766. </table>`);
  2767. return;
  2768. }
  2769.  
  2770. const configuration = {
  2771. drawFileList: false,
  2772. fileListToggle: false,
  2773. fileListStartVisible: false,
  2774. fileContentToggle: false,
  2775. matching: 'lines',
  2776. outputFormat: 'side-by-side', // line-by-line or side-by-side
  2777. synchronisedScroll: true,
  2778. highlight: false,
  2779. renderNothingWhenEmpty: false,
  2780. wordWrap: true,
  2781. stickyFileHeaders: false,
  2782. };
  2783.  
  2784. let $diffHtml = $(Diff2Html.html(Diff.createTwoFilesPatch("a", "b", leftBbcode, rightBbcode), configuration));
  2785. let $diffTbody = $diffHtml.find('tbody.d2h-diff-tbody');
  2786.  
  2787. const processTr = ($tr) => {
  2788. let list = [];
  2789. $tr.each(function () {
  2790. let obj;
  2791. if ($(this).find('td.d2h-code-side-linenumber.d2h-info').length == 1) {
  2792. // 行信息
  2793. const header = $(this).text().trim();
  2794. list.push({ "type": 'header', "content": header });
  2795. } else {
  2796. const number = $(this).find("td[class^='d2h-code-side-linenumber']").text().trim();
  2797. const prefix = $(this).find('span.d2h-code-line-prefix').text();
  2798. let content = $(this).find('span.d2h-code-line-ctn').html();
  2799. content = content === '<br>' ? '' : content;
  2800. const state = (prefix === '+') ? 'add' : ((prefix === '-') ? 'del' : 'no');
  2801. list.push({ "type": 'content', "content": content, "prefix": prefix, "line": number });
  2802. }
  2803. });
  2804. return list;
  2805. }
  2806.  
  2807. let oldList = processTr($diffTbody.eq(0).children('tr'));
  2808. let newList = processTr($diffTbody.eq(1).children('tr'));
  2809.  
  2810. let $html = $(`<table class="diff-table"><tbody id="diff-tbody"></tbody></table>`);
  2811. const $tbody = $html.find('#diff-tbody');
  2812.  
  2813. for (let index = 0; index < oldList.length; index++) {
  2814.  
  2815. if (oldList[index].type === 'header') {
  2816. $tbody.append(`<tr style="background-color: #ddf4ff;">`
  2817. + `<td class="diff-linenumber"></td><td class="diff-text-cell" style="border-right: none;"><span>${oldList[index].content}</span></td>`
  2818. + `<td class="diff-linenumber"></td><td class="diff-text-cell"></td>`
  2819. + `</tr>`);
  2820. continue;
  2821. }
  2822.  
  2823. const oldPrefix = oldList[index].prefix;
  2824. const oldContent = oldList[index].content;
  2825. const oldLine = oldList[index].line;
  2826. const newPrefix = newList[index].prefix;
  2827. const newContent = newList[index].content;
  2828. const newLine = newList[index].line;
  2829.  
  2830. if (oldPrefix === ' ' && newPrefix === ' ') {
  2831. // 两边都没有修改
  2832. $tbody.append(`<tr>`
  2833. + `<td class="diff-linenumber">${oldLine}&nbsp;</td>`
  2834. + `<td class="diff-text-cell">`
  2835. + `<table><tr><td><span class="diff-line-prefix diff-line-prefix-empty"></span></td><td><span class="diff-line-text">${oldContent}</span></td></tr></table>`
  2836. + `</td>`
  2837. + `<td class="diff-linenumber">${newLine}&nbsp;</td>`
  2838. + `<td class="diff-text-cell">`
  2839. + `<table><tr><td><span class="diff-line-prefix diff-line-prefix-empty"></span></td><td><span class="diff-line-text">${newContent}</span></td></tr></table>`
  2840. + `</td>`
  2841. + `</tr>`);
  2842. } else if (oldPrefix === '-' && newPrefix === '+') {
  2843. $tbody.append(`<tr>`
  2844. + `<td class="diff-linenumber diff-linenumber-delete">${oldLine}&nbsp;</td>`
  2845. + `<td class="diff-text-cell diff-line-text-delete">`
  2846. + `<table><tr><td><span class="diff-line-prefix diff-line-prefix-delete"></span></td><td><span class="diff-line-text">${oldContent}</span></td></tr></table>`
  2847. + `</td>`
  2848. + `<td class="diff-linenumber diff-linenumber-insert">${newLine}&nbsp;</td>`
  2849. + `<td class="diff-text-cell diff-line-text-insert">`
  2850. + `<table><tr><td><span class="diff-line-prefix diff-line-prefix-insert"></span></td><td><span class="diff-line-text">${newContent}</span></td></tr></table>`
  2851. + `</td>`
  2852. + `</tr>`);
  2853. } else if (oldPrefix === '-' && newPrefix === ' ') {
  2854. $tbody.append(`<tr>`
  2855. + `<td class="diff-linenumber diff-linenumber-delete">${oldLine}&nbsp;</td>`
  2856. + `<td class="diff-text-cell diff-line-text-delete">`
  2857. + `<table><tr><td><span class="diff-line-prefix diff-line-prefix-delete"></span></td><td><span class="diff-line-text">${oldContent}</span></td></tr></table>`
  2858. + `</td>`
  2859. + `<td class="diff-linenumber diff-linenumber-empty"></td>`
  2860. + `<td class="diff-text-cell-empty"></td>`
  2861. + `</tr>`);
  2862. } else if (oldPrefix === ' ' && newPrefix === '+') {
  2863. $tbody.append(`<tr>`
  2864. + `<td class="diff-linenumber diff-linenumber-empty"></td>`
  2865. + `<td class="diff-text-cell-empty"></td>`
  2866. + `<td class="diff-linenumber diff-linenumber-insert">${newLine}&nbsp;</td>`
  2867. + `<td class="diff-text-cell diff-line-text-insert">`
  2868. + `<table><tr><td><span class="diff-line-prefix diff-line-prefix-insert"></span></td><td><span class="diff-line-text">${newContent}</span></td></tr></table>`
  2869. + `</td>`
  2870. + `</tr>`);
  2871. }
  2872.  
  2873. }
  2874.  
  2875. drawElement.html($html);
  2876. }