U2历史记录

查看种子历史记录

目前为 2023-01-26 提交的版本。查看 最新版本

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

QingJ © 2025

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