Greasy Fork is available in English.

WoD 物品标记

阿沐的物品标记,被标记的物品很大概率会被处理给钱袋

  1. // ==UserScript==
  2. // @name WoD 物品标记
  3. // @icon http://info.world-of-dungeons.org/wod/css/WOD.gif
  4. // @namespace WOD_Tools
  5. // @description 阿沐的物品标记,被标记的物品很大概率会被处理给钱袋
  6. // @author Christophero
  7. // @include http*://*.world-of-dungeons.org/wod/spiel/hero/items.php?*
  8. // @license MIT License
  9. // @require https://code.jquery.com/jquery-3.3.1.min.js
  10. // @require https://cdn.jsdelivr.net/npm/slim-select@2.4.5/dist/slimselect.umd.min.js
  11. // @resource slimselect_css https://cdn.jsdelivr.net/npm/slim-select@2.4.5/dist/slimselect.min.css
  12. // @grant GM_addStyle
  13. // @grant GM_getResourceText
  14. // @connect www.christophero.xyz
  15. // @modifier Christophero
  16. // @version 2023.04.16.2
  17. // ==/UserScript==
  18.  
  19. (function () {
  20. ("use strict");
  21.  
  22. const MARK_ITEM_OPTION_KEY = "MARK_ITEM_OPTION_KEY";
  23.  
  24. initComponent();
  25.  
  26. function saveOption(option) {
  27. return localStorage.setItem(MARK_ITEM_OPTION_KEY, JSON.stringify(option));
  28. }
  29.  
  30. function loadOption() {
  31. let localMarkItemOption = {};
  32. const localMarkItemOptionText = localStorage.getItem(MARK_ITEM_OPTION_KEY);
  33. if (!localMarkItemOptionText) {
  34. localMarkItemOption = {
  35. blue: {
  36. color: "#3498DB",
  37. label: "蓝色",
  38. items: [],
  39. },
  40. green: {
  41. color: "#28B463",
  42. label: "绿色",
  43. items: [],
  44. },
  45. red: {
  46. color: "#E74C3C",
  47. label: "红色",
  48. items: [],
  49. },
  50. orange: {
  51. color: "#D35400",
  52. label: "橙色",
  53. items: [],
  54. },
  55. };
  56. } else {
  57. localMarkItemOption = JSON.parse(localMarkItemOptionText);
  58. }
  59. return localMarkItemOption;
  60. }
  61.  
  62. /**
  63. * 导出文件的方法,导出并直接进行下载
  64. *
  65. * @param {String} 传入导出文件的数据, 格式为字符串
  66. * @param {String} 导出文件的文件名称
  67. */
  68. const exportFile = (
  69. text = "",
  70. filename = "分析内容.txt",
  71. exportCsv = false
  72. ) => {
  73. let blob;
  74. // 导出数据
  75. if (exportCsv) {
  76. blob = new Blob(["\ufeff" + text], {
  77. type: "text/csv;charset=utf-8;",
  78. });
  79. } else {
  80. blob = new Blob([text]);
  81. }
  82. const e = new MouseEvent("click");
  83. const a = document.createElement("a");
  84.  
  85. a.download = filename;
  86. a.href = window.URL.createObjectURL(blob);
  87. a.dispatchEvent(e);
  88. };
  89.  
  90. function exportOption() {
  91. exportFile(JSON.stringify(loadOption()), "物品标记.json");
  92. }
  93.  
  94. const importFileJSON = (ev) => {
  95. return new Promise((resolve, reject) => {
  96. const fileDom = ev.target,
  97. file = fileDom.files[0];
  98.  
  99. // 格式判断
  100. if (file.type !== "application/json") {
  101. reject("仅允许上传json文件");
  102. }
  103. // 检验是否支持FileRender
  104. if (typeof FileReader === "undefined") {
  105. reject("当前浏览器不支持FileReader");
  106. }
  107.  
  108. // 执行后清空input的值,防止下次选择同一个文件不会触发onchange事件
  109. ev.target.value = "";
  110.  
  111. // 执行读取json数据操作
  112. const reader = new FileReader();
  113. reader.readAsText(file); // 读取的结果还有其他读取方式,我认为text最为方便
  114.  
  115. reader.onerror = (err) => {
  116. reject("json文件解析失败", err);
  117. };
  118.  
  119. reader.onload = () => {
  120. const resultData = reader.result;
  121. if (resultData) {
  122. try {
  123. const importData = JSON.parse(resultData);
  124. resolve(importData);
  125. } catch (error) {
  126. reject("读取数据解析失败", error);
  127. }
  128. } else {
  129. reject("读取数据解析失败", error);
  130. }
  131. };
  132. });
  133. };
  134.  
  135. function importOption(event) {
  136. importFileJSON(event)
  137. .then((option) => {
  138. console.log("读取到的数据", option);
  139. saveOption(option);
  140. alert("导入物品标记数据成功,刷新页面");
  141. location.reload();
  142. })
  143. .catch((err) => {
  144. console.log(err);
  145. });
  146. }
  147.  
  148. //
  149. function initComponent() {
  150. GM_addStyle(GM_getResourceText("slimselect_css"));
  151. GM_addStyle(
  152. ".ss-main.mark-select {display: inline-flex !important; width: 120px;}"
  153. );
  154.  
  155. const itemSelectMap = {};
  156. let localMarkItemOption = loadOption();
  157. let markItemMap = {};
  158. const markData = [{ html: "<b>无标记</b>", text: "无标记", value: "" }];
  159. for (let key of Object.keys(localMarkItemOption)) {
  160. let colorOption = localMarkItemOption[key];
  161. markData.push({
  162. html: `<b style="color: ${colorOption.color}">${colorOption.label}</b>`,
  163. text: colorOption.label,
  164. value: key,
  165. });
  166. colorOption.items.forEach((itemName) => {
  167. markItemMap[itemName] = key;
  168. });
  169. }
  170. $(".layout_clear .content_table a[href*='item_instance_id']").each(
  171. function (i, e) {
  172. const params = $(e).attr("href").split("?")[1];
  173. const instanceId = new URLSearchParams(params).get("item_instance_id");
  174. // 道具名称
  175. const itemName = e.text.replace(/!$/, "");
  176. const $td = $("<td></td>");
  177. const $checkbox = $(`<input type="checkbox" class="check-to-mark">`)
  178. .data("instanceid", instanceId)
  179. .data("itemname", itemName);
  180. const $select = $(`<select class="mark-select"></select>`);
  181.  
  182. $td.append($select, $checkbox);
  183. $(e).parents("tr:first").append($td);
  184. const copyMarkData = JSON.parse(JSON.stringify(markData));
  185. for (let opt of copyMarkData) {
  186. if (opt.value === markItemMap[itemName]) {
  187. opt.selected = true;
  188. }
  189. }
  190. const slimSelect = new SlimSelect({
  191. select: $select[0],
  192. value: markItemMap[itemName],
  193. data: copyMarkData,
  194. settings: {
  195. showSearch: false,
  196. },
  197. events: {
  198. beforeChange: ([{ value: newVal }], [{ value: oldVal }]) => {
  199. console.log(oldVal);
  200. console.log(newVal);
  201. // 有值切换到无值,则删除存储数据
  202. if (oldVal) {
  203. const originColor = markItemMap[itemName];
  204. const items = localMarkItemOption[originColor].items;
  205. items.splice(
  206. items.findIndex((item) => item === itemName),
  207. 1
  208. );
  209. delete markItemMap[itemName];
  210. saveOption(localMarkItemOption);
  211. }
  212. if (newVal) {
  213. if (!localMarkItemOption[newVal].items.includes(itemName)) {
  214. localMarkItemOption[newVal].items.push(itemName);
  215. }
  216. markItemMap[itemName] = newVal;
  217. }
  218. saveOption(localMarkItemOption);
  219. // return false; // this will stop the change from happening
  220. },
  221. },
  222. });
  223. itemSelectMap[instanceId] = slimSelect;
  224. }
  225. );
  226.  
  227. // 添加导入导出与标记物品勾选
  228. const $itemMarkSelect = $('<select class="mark-select"></select>');
  229. const $exportBtn = $(
  230. '<input type="button" name="export" value="导出" class="button clickable">'
  231. ).click(exportOption);
  232. const $fileBtn = $(
  233. '<input type="file" id="cleanFile" name="cleanFile" runat="server" style="position:relative; z-index:10; filter:alpha(opacity=0);-moz-opacity:0; opacity:0; width: 65px; left:-65px;">'
  234. ).change(importOption);
  235. const $importBtn = $(
  236. '<input type="button" name="button" value="导入" class="button clickable" style="width:65px;">'
  237. );
  238. const $pageRow = $(".layout_clear .content_table .paginator_row.top");
  239. const $label = $("<span>&nbsp;&nbsp;&nbsp;物品标记:</span>");
  240. $pageRow.append($label, $itemMarkSelect, $exportBtn, $importBtn, $fileBtn);
  241.  
  242. const itemMarkSelect = new SlimSelect({
  243. select: $itemMarkSelect[0],
  244. data: markData,
  245. settings: {
  246. showSearch: false,
  247. },
  248. events: {
  249. afterChange: ([{ value: newVal }]) => {
  250. let needCheckedItems = localMarkItemOption[newVal];
  251. if (!needCheckedItems) return;
  252. needCheckedItems = needCheckedItems.items;
  253. $(".layout_clear .content_table a[href*='item_instance_id']").each(
  254. (i, e) => {
  255. const itemName = e.text.replace(/!$/, "");
  256. $(e)
  257. .parents("tr:first")
  258. .find('input[name^="doEquipItem["]')
  259. .prop("checked", needCheckedItems.includes(itemName));
  260. }
  261. );
  262. },
  263. },
  264. });
  265.  
  266. // 添加标记列
  267. const $markTableHeader = $("<th></th>");
  268. const $markAllChk = $("<input type='checkbox' class='mark-all'>");
  269. const $batchSelect = $('<select class="mark-select"></select>');
  270. $(".layout_clear .content_table thead tr.header").append(
  271. $markTableHeader.append($batchSelect, $markAllChk)
  272. );
  273. $markAllChk.change(function () {
  274. if ($(this).is(":checked")) {
  275. $("input.check-to-mark").prop("checked", true);
  276. } else {
  277. $("input.check-to-mark").prop("checked", false);
  278. }
  279. });
  280. const batchSelect = new SlimSelect({
  281. select: $batchSelect[0],
  282. data: markData,
  283. settings: {
  284. showSearch: false,
  285. },
  286. events: {
  287. beforeChange: ([{ value: newVal }], [{ value: oldVal }]) => {
  288. $("input.check-to-mark:checked").each((i, e) => {
  289. const instanceId = $(e).data("instanceid");
  290. const itemName = $(e).data("itemname");
  291. select = itemSelectMap[instanceId];
  292. const preVal = select.getSelected()[0];
  293. select.setSelected(newVal);
  294. // 有值切换到无值,则删除存储数据
  295. if (preVal) {
  296. const originColor = markItemMap[itemName];
  297. const items = localMarkItemOption[originColor].items;
  298. items.splice(
  299. items.findIndex((item) => item === itemName),
  300. 1
  301. );
  302. delete markItemMap[itemName];
  303. }
  304. if (newVal) {
  305. if (!localMarkItemOption[newVal].items.includes(itemName)) {
  306. localMarkItemOption[newVal].items.push(itemName);
  307. }
  308. markItemMap[itemName] = newVal;
  309. }
  310. });
  311. // 有值切换到无值,则删除存储数据
  312. saveOption(localMarkItemOption);
  313. // return false; // this will stop the change from happening
  314. },
  315. // afterChange: ([{ value: newVal }]) => {
  316. // $("input.check-to-mark:checked").each((i, e) => {
  317. // const instanceId = $(e).data("instanceid");
  318. // select = itemSelectMap[instanceId];
  319. // select.setSelected(newVal);
  320. // });
  321. // },
  322. },
  323. });
  324. }
  325. })();