Luogu Tasks

在洛谷侧边栏显示题单与自己存的题

Fra 25.05.2021. Se den seneste versjonen.

  1. // ==UserScript==
  2. // @name Luogu Tasks
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.4
  5. // @run-at document-start
  6. // @description 在洛谷侧边栏显示题单与自己存的题
  7. // @author __OwO__
  8. // @match https://www.luogu.com.cn/*
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant unsafeWindow
  12. // @require https://cdn.staticfile.org/jquery/3.5.1/jquery.min.js
  13. // @require https://cdn.staticfile.org/sweetalert/2.1.2/sweetalert.min.js
  14. // ==/UserScript==
  15.  
  16. // == define config ==
  17.  
  18. const inject_lantency = 500; // in ms
  19.  
  20. // configs store in browser
  21. let configs = {
  22. set train(v) {
  23. GM_setValue("problem-helper-local-train-list", v);
  24. },
  25. get train() {
  26. return GM_getValue("problem-helper-local-train-list") || [];
  27. },
  28. set local(v) {
  29. GM_setValue("problem-helper-local-id", v);
  30. },
  31. get local() {
  32. return GM_getValue("problem-helper-local-id") || null;
  33. },
  34. set hide_ac(v) {
  35. GM_setValue("problem-helper-hide-aced", v);
  36. },
  37. get hide_ac() {
  38. return GM_getValue("problem-helper-hide-aced") || false;
  39. },
  40. };
  41.  
  42. // == end config ==
  43.  
  44. // == lib functions ==
  45.  
  46. // get task info
  47. function getTask(id) {
  48. return new Promise((r) => {
  49. $.get(`https://www.luogu.com.cn/training/${id}?_contentOnly=any`).then(
  50. (u) => r(u.currentData.training)
  51. );
  52. });
  53. }
  54.  
  55. // save train list
  56. let saveTrain = async (problems) => {
  57. return new Promise((r) => {
  58. $.ajax({
  59. type: "POST",
  60. url: `https://www.luogu.com.cn/api/training/editProblems/${configs.local}`,
  61. beforeSend: function (request) {
  62. request.setRequestHeader(
  63. "x-csrf-token",
  64. $("meta[name='csrf-token']")[0].content
  65. );
  66. },
  67. contentType: "application/json;charset=UTF-8",
  68. data: JSON.stringify({ pids: problems }),
  69. success: () => r(),
  70. });
  71. });
  72. };
  73.  
  74. // save config
  75. let saveConfig = async (config) => {
  76. return new Promise((r) => {
  77. $.ajax({
  78. type: "POST",
  79. url: `https://www.luogu.com.cn/paste/new`,
  80. beforeSend: function (request) {
  81. request.setRequestHeader(
  82. "x-csrf-token",
  83. $("meta[name='csrf-token']")[0].content
  84. );
  85. },
  86. contentType: "application/json;charset=UTF-8",
  87. data: JSON.stringify({
  88. public: false,
  89. data: "#lgtsk" + JSON.stringify(config),
  90. }),
  91. success: () => r(),
  92. });
  93. });
  94. };
  95.  
  96. // load config
  97. let loadConfig = () => {
  98. return new Promise((r) => {
  99. $.get("https://www.luogu.com.cn/paste?_contentOnly").then((u) => {
  100. u = u.currentData.pastes.result;
  101. let nc = null;
  102. for (let i in u) {
  103. try {
  104. if (u[i].data.substr(0, 6) !== "#lgtsk") continue;
  105. let k = u[i].data;
  106. nc = JSON.parse(k.substr(6, k.lentgh));
  107. break;
  108. } catch (e) {}
  109. }
  110. if (!nc) return r(0);
  111. configs.train = nc.train;
  112. configs.local = nc.local;
  113. configs.hide_ac = nc.hide_ac;
  114. r(1);
  115. });
  116. });
  117. };
  118.  
  119. // == end lib functions ==
  120.  
  121. $(document).ready(async () => {
  122. // == hook pushState ==
  123. var _wr = (type) => {
  124. var orig = history[type];
  125. return function () {
  126. var e = new Event(type);
  127. e.arguments = arguments;
  128. window.dispatchEvent(e);
  129. var rv = orig.apply(this, arguments);
  130. return rv;
  131. };
  132. };
  133. history.pushState = _wr("pushState");
  134. history.replaceState = _wr("replaceState");
  135.  
  136. let local_problems;
  137.  
  138. // == get verdict icon ==
  139. let geticon = (pid, uid, your_score, full_score, is_aced = null) => {
  140. let res;
  141. let ua = (uid, pid) =>
  142. `<a data-v-303bbf52="" data-v-357e29e4="" href="/record/list?pid=${pid}&amp;user=${uid}" target="_blank" colorscheme="default" class="color-default" data-v-83961efe="" style="color: inherit; float:left; padding-right: 0.5em;"><svg data-v-1b44b3e6="" data-v-357e29e4="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512" class="icon svg-inline--fa fa-times fa-w-11" data-v-303bbf52="" style="transform: scale(1.2); color: rgb(231, 76, 60);"><path data-v-1b44b3e6="" fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" class=""></path></svg></a>`;
  143. let ac = (uid, pid) =>
  144. `<a data-v-303bbf52="" data-v-357e29e4="" href="/record/list?pid=${pid}&amp;user=${uid}" target="_blank" colorscheme="default" class="color-default" data-v-83961efe="" style="color: inherit; float:left; padding-right: 0.5em;"><svg data-v-1b44b3e6="" data-v-357e29e4="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="check" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="icon svg-inline--fa fa-check fa-w-16" data-v-303bbf52="" style="color: rgb(82, 196, 26);"><path data-v-1b44b3e6="" fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z" class=""></path></svg></a>`;
  145. let nt = (uid, pid) =>
  146. `<a data-v-303bbf52="" data-v-357e29e4="" href="/record/list?pid=${pid}&amp;user=${uid}" target="_blank" colorscheme="default" class="color-default" data-v-83961efe="" style="color: inherit; float:left; padding-right: 0.5em;"><svg data-v-1b44b3e6="" data-v-357e29e4="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="minus" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="icon svg-inline--fa fa-minus fa-w-14" data-v-303bbf52="" style="opacity: 0.7;"><path data-v-1b44b3e6="" fill="currentColor" d="M416 208H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z" class=""></path></svg></a>`;
  147. if (your_score == full_score || is_aced) res = ac(uid, pid);
  148. else if (your_score == null) res = nt(uid, pid);
  149. else res = ua(uid, pid);
  150. return `<span data-v-3fb75f36="" style="font-weight: bold;">${res}</span>`;
  151. };
  152.  
  153. // == render one list ==
  154. let renderList = (title, content, float, id = "") =>
  155. `
  156. <div
  157. data-v-796309f8=""
  158. data-v-3fb75f36=""
  159. class="card padding-default problem-helper-container"
  160. data-v-6febb0e8=""
  161. id="list-${id}"
  162. >
  163. <div id="list-float-${id}">${float}</div>
  164. <h4 data-v-3fb75f36="" data-v-796309f8="" class="lfe-h4" >${title}</h4>
  165. <div class="problem-helper-inner" style="display:none;">
  166. <div id="list-content-${id}">${content}</div>
  167. </div>
  168. <div data-v-e4b7c2ca="" data-v-3fb75f36="" class="expand-tip lfe-caption" data-v-796309f8="" >
  169. <span class="problem-helper-fold-off" style="display:none;" data-v-e4b7c2ca=""><svg data-v-e4b7c2ca="" aria-hidden="true" focusable="false" data-prefix="fal" data-icon="chevron-up" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-chevron-up fa-w-14" ><path data-v-e4b7c2ca="" fill="currentColor" d="M4.465 366.475l7.07 7.071c4.686 4.686 12.284 4.686 16.971 0L224 178.053l195.494 195.493c4.686 4.686 12.284 4.686 16.971 0l7.07-7.071c4.686-4.686 4.686-12.284 0-16.97l-211.05-211.051c-4.686-4.686-12.284-4.686-16.971 0L4.465 349.505c-4.687 4.686-4.687 12.284 0 16.97z" class=""></path></svg> 隐藏列表</span>
  170. <span class="problem-helper-fold-on" data-v-e4b7c2ca=""><svg data-v-e4b7c2ca="" aria-hidden="true" focusable="false" data-prefix="fal" data-icon="chevron-down" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-chevron-down fa-w-14"><path data-v-e4b7c2ca="" fill="currentColor" d="M443.5 162.6l-7.1-7.1c-4.7-4.7-12.3-4.7-17 0L224 351 28.5 155.5c-4.7-4.7-12.3-4.7-17 0l-7.1 7.1c-4.7 4.7-4.7 12.3 0 17l211 211.1c4.7 4.7 12.3 4.7 17 0l211-211.1c4.8-4.7 4.8-12.3.1-17z" class=""></path></svg> 查看列表</span>
  171. </div>
  172. </div>
  173. `;
  174. let loadProblemList = async (id) => {
  175. let task;
  176. let getList = (x, is_local) => {
  177. let id = task.problems[x].problem.pid;
  178. if (configs.hide_ac && task.userScore.status[id] && id != configs.local)
  179. return "";
  180. return `
  181. <div>
  182. <div>
  183. <span data-v-3a151854="">
  184. <a
  185. data-v-303bbf52=""
  186. data-v-3fb75f36=""
  187. href="/record/list?pid=${id}&amp;user=${task.userScore.user.uid}"
  188. class="color-default"
  189. style="text-decoration: none;"
  190. >
  191. ${geticon(
  192. id,
  193. task.userScore.user.uid,
  194. task.userScore.score[id],
  195. task.problems[x].problem.fullScore,
  196. task.userScore.status[id]
  197. )}
  198. </a>
  199. ${
  200. is_local
  201. ? `
  202. <a
  203. data-v-3fb75f36=""
  204. style="float: right;font-weight: light;"
  205. class="problem-helper-delete-from-list"
  206. data="${id}"
  207. >
  208. <svg data-v-4121e124="" aria-hidden="true" focusable="false" data-prefix="fal" data-icon="times" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-times fa-w-10"><path data-v-4121e124="" fill="currentColor" d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z" class=""></path></svg>
  209. </a>
  210. `
  211. : ""
  212. }
  213. </span>
  214. <a
  215. class="colored problem-helper-text"
  216. style="padding-left: 3px"
  217. href="/problemnew/show/${id}"
  218. target="_blank"
  219. >
  220. <b>${id}</b> ${task.problems[x].problem.title}
  221. </a>
  222. </div>
  223. </div>`;
  224. };
  225. async function loadContent(id) {
  226. task = await getTask(id);
  227. let content = "";
  228. if (id == configs.local) {
  229. $(`#list-float-${id}`).text("");
  230. local_problems = task.problems.map((u) => u.problem.pid);
  231. }
  232. for (let i in task.problems) content += getList(i, id == configs.local);
  233. $(`#list-content-${id}`).html(content);
  234. $(`#list-${id} > h4`).text(task.title);
  235. }
  236. loadContent(id);
  237. return renderList(
  238. "...",
  239. "...",
  240. '<a style="float:right;" data="${task.id}" data-v-303bbf52="" data-v-3fb75f36="" href="javascript:void 0" class="problem-helper-train-remove color-default">删除</a>',
  241. id
  242. );
  243. };
  244. let getScore = async (sett) => {
  245. if (sett.method == "train")
  246. return {
  247. uid: sett.task.userScore.user.uid,
  248. scr: sett.task.userScore.score[sett.id],
  249. fscr: sett.task.problems[sett.id].problem.fullScore,
  250. };
  251. else if (sett.method == "local")
  252. return new Promise((res, rej) => {
  253. $.get(`https://www.luogu.com.cn/problem/${sett.id}?_contentOnly`).then(
  254. (u) => {
  255. res({
  256. uid: u.currentUser.uid,
  257. scr: u.currentData.problem.score,
  258. fscr: u.currentData.problem.fullScore,
  259. });
  260. }
  261. );
  262. });
  263. };
  264. // let loadLocalProblem = async () => {
  265. // let local = GM_getValue("problem-helper-local-list");
  266. // let getList = async (id, opt = 1) => {
  267. // let h = "";
  268. // if (opt) {
  269. // let info = await getScore({ method: "local", id: id });
  270. // h = geticon(info.uid, id, info.scr, info.fscr);
  271. // }
  272. // return `
  273. // <div>
  274. // <div>
  275. // ${h}
  276. // <span data-v-3a151854="">
  277. // <a
  278. // data-v-303bbf52=""
  279. // data-v-3fb75f36=""
  280. // href="javascript:void 0"
  281. // class="color-default"
  282. // style="text-decoration: none;"
  283. // >
  284. // <span
  285. // data-v-3fb75f36=""
  286. // style="float: right;font-weight: light;"
  287. // data="${id}"
  288. // class="problem-helper-delete-from-list"
  289. // >
  290. // 删除
  291. // </span>
  292. // </a>
  293. // </span>
  294. // <a
  295. // class="colored problem-helper-text
  296. // style="padding-left: 3px"
  297. // href="/problemnew/show/${id}"
  298. // target="_blank"
  299. // >
  300. // <b>${id}</b> ${local[id]}
  301. // </a>
  302. // </div>
  303. // </div>`;
  304. // };
  305. // let content = "";
  306. // for (let i in local) content += await getList(i);
  307. // return renderList("本地列表", content, "");
  308. // };
  309. function renderItem(title, id) {
  310. return `
  311. <div>
  312. <div>
  313. <span data-v-3a151854="">
  314. <a
  315. data-v-303bbf52=""
  316. data-v-3fb75f36=""
  317. href="/record/list?pid=${id}&amp;user=${task.userScore.user.uid}"
  318. class="color-default"
  319. style="text-decoration: none;"
  320. >
  321. ${geticon(
  322. id,
  323. task.userScore.user.uid,
  324. task.userScore.score[id],
  325. task.problems[x].problem.fullScore,
  326. task.userScore.status[id]
  327. )}
  328. </a>
  329. </span>
  330. <a
  331. class="colored problem-helper-text
  332. style="padding-left: 3px"
  333. href="/problemnew/show/${id}"
  334. target="_blank"
  335. >
  336. <b>${id}</b> ${task.problems[x].problem.title}
  337. </a>
  338. </div>
  339. </div>
  340. `;
  341. }
  342. async function loaderProblemEntry() {
  343. let lists = "";
  344. let urls = configs.train;
  345. // console.log(urls);
  346. if (configs.local && !urls[configs.local]) urls[configs.local] = "本地列表";
  347. for (let i in urls) lists += await loadProblemList(i);
  348. lists += renderList(
  349. "设置",
  350. `<div data-v-59a1d633="" data-v-83961efe="" class="row">
  351. <div data-v-72d91c56="" data-v-59a1d633="" class="checkbox" data-v-83961efe=""><input id="problem-helper-hide-aced" type="checkbox" value="65560" ${
  352. configs.hide_ac ? 'checked=""' : ""
  353. }"> <lable>
  354. 隐藏已通过题目
  355. </label> </div>
  356. <div>
  357. <label>本地题单</label>
  358. <input data-v-a7f7c968="" type="text" placeholder="题单id" class="lfe-form-sz-middle" style="width: 30%;" value="${
  359. configs.local || ""
  360. }" id="problem-helper-set-local-list">
  361. </div>
  362. <div>
  363. <a id="problem-helper-save">保存到洛谷云剪贴板</a>
  364. </div>
  365. <div>
  366. <a id="problem-helper-load">从洛谷云剪贴板加载</a>
  367. </div>
  368. <div>
  369. <label>批量导入</label>
  370. <input data-v-a7f7c968="" type="text" class="lfe-form-sz-middle" style="width: 30%;" id="problem-helper-import">
  371. </div>
  372. </div>`,
  373. ""
  374. );
  375. $("#problem-helper-entry").html(lists);
  376. $(".problem-helper-fold-on").click((u) => {
  377. u = $(u.target);
  378. u.parent().prev(".problem-helper-inner").removeAttr("style");
  379. u.attr("style", "display:none;");
  380. u.prev(".problem-helper-fold-off").removeAttr("style");
  381. });
  382. $(".problem-helper-fold-off").click((u) => {
  383. u = $(u.target);
  384. u.parent().prev(".problem-helper-inner").attr("style", "display:none;");
  385. u.attr("style", "display:none;");
  386. u.next(".problem-helper-fold-on").removeAttr("style");
  387. });
  388. $("#problem-helper-hide-aced").click((u) => {
  389. u = $(u.target);
  390. configs.hide_ac = u[0].checked;
  391. unsafeWindow._feInstance.$swalToastSuccess("修改成功");
  392. loaderProblemEntry();
  393. });
  394. $("#problem-helper-save").click((u) => {
  395. saveConfig(configs);
  396. unsafeWindow._feInstance.$swalToastSuccess("保存成功");
  397. loaderProblemEntry();
  398. });
  399. $("#problem-helper-load").click(async (u) => {
  400. if (await loadConfig())
  401. unsafeWindow._feInstance.$swalToastSuccess("加载成功");
  402. else unsafeWindow._feInstance.$swalToastError("加载失败");
  403. loaderProblemEntry();
  404. });
  405. $("#problem-helper-set-local-list").keydown(function (e) {
  406. if (e.keyCode == 13) {
  407. configs.local = $("#problem-helper-set-local-list").val();
  408. unsafeWindow._feInstance.$swalToastSuccess("修改成功");
  409. loaderProblemEntry();
  410. }
  411. });
  412. $("#problem-helper-import").keydown(function (e) {
  413. if (e.keyCode == 13) {
  414. let u = $("#problem-helper-import").val().trim().split(",");
  415. let t = local_problems;
  416. for (let i in u) if (t.indexOf(u[i]) == -1) t.push(u[i]);
  417. saveTrain(t);
  418. unsafeWindow._feInstance.$swalToastSuccess("导入成功");
  419. loaderProblemEntry();
  420. }
  421. });
  422. $("#problem-helper-add-to-list").click(async (u) => {
  423. let r = unsafeWindow.location.href.split("/");
  424. r = r[r.length - 1];
  425. while (r[r.length - 1] == "#") r = r.slice(0, r.length - 1);
  426. if (!unsafeWindow._feInjection.currentData.problem.title) return;
  427. local_problems = (await getTask(configs.local)).problems.map(
  428. (u) => u.problem.pid
  429. );
  430. if (local_problems.indexOf(r) == -1) local_problems.push(r);
  431. saveTrain(local_problems);
  432. unsafeWindow._feInstance.$swalToastSuccess("添加成功");
  433. loaderProblemEntry();
  434. });
  435. $(".problem-helper-delete-from-list").click((u) => {
  436. u = $(u.target).parents(".problem-helper-delete-from-list");
  437. let r = u.attr("data");
  438. let now = local_problems;
  439. let newone = [];
  440. for (let i in now) if (now[i] != r) newone.push(now[i]);
  441. saveTrain(newone);
  442. unsafeWindow._feInstance.$swalToastSuccess("删除成功");
  443. loaderProblemEntry();
  444. });
  445. $(".problem-helper-train-remove").click((u) => {
  446. u = $(u.target);
  447. let r = u.attr("data");
  448. let now = configs.train;
  449. let newone = {};
  450. for (let i in now) if (i != r) newone[i] = now[i];
  451. configs.train = now;
  452. unsafeWindow._feInstance.$swalToastSuccess("删除成功");
  453. loaderProblemEntry();
  454. });
  455. }
  456. let deferredInjectProblemPage = () => {
  457. $(".problem-helper-container").remove();
  458. $(".side").prepend(
  459. renderList(
  460. "做题助手",
  461. '<div id="problem-helper-entry"></div>',
  462. '<a style="float:right;" data-v-303bbf52="" data-v-3fb75f36="" href="javascript:void 0" id="problem-helper-add-to-list" class="color-default"><svg data-v-b35188f4="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="plus-square" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-plus-square fa-w-14"><path data-v-b35188f4="" fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z" class=""></path></svg>添加至列表</a >'
  463. )
  464. );
  465. loaderProblemEntry();
  466. };
  467. let loadTrainList = (id) => {
  468. let local = configs.train;
  469. let getList = (id) => {
  470. return `
  471. <div>
  472. <div>
  473. <span data-v-3a151854="">
  474. <a
  475. style="text-decoration: none;float: right;font-weight: light;"
  476. data="${id}"
  477. class="color-default problem-helper-delete-from-list"
  478. data-v-303bbf52=""
  479. data-v-3fb75f36=""
  480. href="javascript:void 0"
  481. >
  482. <svg data-v-4121e124="" aria-hidden="true" focusable="false" data-prefix="fal" data-icon="times" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-times fa-w-10"><path data-v-4121e124="" fill="currentColor" d="M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z" class=""></path></svg>
  483. </a>
  484. </span>
  485. <a
  486. class="colored problem-helper-text"
  487. style="padding-left: 3px"
  488. href="https://www.luogu.com.cn/training/${id}#information"
  489. target="_blank"
  490. >
  491. <b>${id}</b> ${local[id]}
  492. </a>
  493. </div>
  494. </div>`;
  495. };
  496. let lists = "";
  497. for (let i in local) lists += getList(i);
  498. return lists;
  499. };
  500. let loaderTrainEntry = async () => {
  501. let lists = loadTrainList();
  502. $("#problem-helper-entry").html(lists);
  503. $(".problem-helper-fold-on").click((u) => {
  504. u = $(u.target);
  505. u.parent().prev(".problem-helper-inner").removeAttr("style");
  506. u.attr("style", "display:none;");
  507. u.prev(".problem-helper-fold-off").removeAttr("style");
  508. });
  509. $(".problem-helper-fold-off").click((u) => {
  510. u = $(u.target);
  511. u.parent().prev(".problem-helper-inner").attr("style", "display:none;");
  512. u.attr("style", "display:none;");
  513. u.next(".problem-helper-fold-on").removeAttr("style");
  514. });
  515. $("#problem-helper-add-to-list").click((u) => {
  516. let r = unsafeWindow.location.href.split("/");
  517. r = r[r.length - 1].split("#")[0];
  518. if (!unsafeWindow._feInjection.currentData.training.title) return;
  519. let now = configs.train;
  520. if (!now) now = {};
  521. now[r] = unsafeWindow._feInjection.currentData.training.title;
  522. configs.train = now;
  523. unsafeWindow._feInstance.$swalToastSuccess("添加成功");
  524. loaderTrainEntry();
  525. });
  526. $(".problem-helper-delete-from-list").click((u) => {
  527. u = $(u.target).parents(".problem-helper-delete-from-list");
  528. let r = u.attr("data");
  529. let now = configs.train;
  530. let newone = {};
  531. for (let i in now) if (i != r) newone[i] = now[i];
  532. configs.train = newone;
  533. unsafeWindow._feInstance.$swalToastSuccess("删除成功");
  534. loaderProblemEntry();
  535. });
  536. };
  537. let deferredInjectTrainPage = () => {
  538. $(".problem-helper-container").remove();
  539. $(".side").prepend(
  540. renderList(
  541. "做题助手",
  542. '<div id="problem-helper-entry"></div>',
  543. '<a style="float:right;" data-v-303bbf52="" data-v-3fb75f36="" href="javascript:void 0" id="problem-helper-add-to-list" class="color-default"><svg data-v-b35188f4="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="plus-square" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-plus-square fa-w-14"><path data-v-b35188f4="" fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z" class=""></path></svg>添加至列表</a>'
  544. )
  545. );
  546. loaderTrainEntry();
  547. };
  548. let deferredInjectProblemlist = async () => {
  549. local_problems = (await getTask(configs.local)).problems.map(
  550. (u) => u.problem.pid
  551. );
  552. $(".problem-helper-inlist-adder").remove();
  553. let pid = "",
  554. name = "";
  555. let h = (pid, name) => `
  556. <a style="float:right; padding-right: 2em;" data="${pid}" data-v-303bbf52="" data-v-3fb75f36="" href="javascript:void 0" class="problem-helper-inlist-adder color-default"><svg data-v-b35188f4="" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="plus-square" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="svg-inline--fa fa-plus-square fa-w-14"><path data-v-b35188f4="" fill="currentColor" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm-32 252c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92H92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z" class=""></path></svg>
  557. 添加
  558. </a>`;
  559. let rows = $(".row");
  560. let trim = (s) => {
  561. return s.replace(/(^\s*)|(\s*$)/g, "");
  562. };
  563. rows.each((u) => {
  564. u = $(rows[u]);
  565. pid = u.children(".pid").text();
  566. name = u.children(".title").children(".title").text();
  567. name = trim(name);
  568. u.children(".title").prepend(h(pid, name));
  569. });
  570. $(".problem-helper-inlist-adder").click((u) => {
  571. u = $(u.target);
  572. let r = u.attr("data");
  573. let now = local_problems;
  574. if (now.indexOf(r) == -1) now.push(r);
  575. saveTrain(now);
  576. unsafeWindow._feInstance.$swalToastSuccess("添加成功");
  577. });
  578. };
  579.  
  580. /* main controller */
  581. function inject() {
  582. if (unsafeWindow.location.href.includes("problem/list"))
  583. deferredInjectProblemlist();
  584. else if (unsafeWindow.location.href.includes("training"))
  585. deferredInjectTrainPage(), deferredInjectProblemlist();
  586. else if (unsafeWindow.location.href.includes("problem"))
  587. deferredInjectProblemPage();
  588. }
  589. window.addEventListener("pushState", function (e) {
  590. setTimeout(inject, inject_lantency);
  591. });
  592. window.addEventListener("replaceState", function (e) {
  593. setTimeout(inject, inject_lantency);
  594. });
  595. setTimeout(inject, 100);
  596. $(document.body).append(
  597. `<style>
  598. .problem-helper-text{
  599. -webkit-line-clamp: 1; overflow: hidden; display: -webkit-box;
  600. -webkit-box-orient: vertical; white-space: normal;
  601. }
  602. .expand-tip > span[data-v-e4b7c2ca] {
  603. -webkit-user-select: none;
  604. -moz-user-select: none;
  605. -ms-user-select: none;
  606. cuser-select: none;
  607. cursor: pointer;
  608. color: rgba(0, 0, 0, .3);
  609. }
  610. .expand-tip[data-v-e4b7c2ca] {
  611. text-align: center;
  612. }
  613. .expand-tip > span[data-v-e4b7c2ca]:hover {
  614. color: inherit;
  615. }
  616. </style>`
  617. );
  618. });