Customizable Bazaar Filler

On click, auto-fills bazaar item quantities and prices based on your preferences

当前为 2025-06-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Customizable Bazaar Filler
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.80
  5. // @description On click, auto-fills bazaar item quantities and prices based on your preferences
  6. // @match https://www.torn.com/bazaar.php*
  7. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_xmlhttpRequest
  12. // @connect weav3r.dev
  13. // ==/UserScript==
  14.  
  15.  
  16. (function () {
  17. "use strict";
  18.  
  19. function handleError(error, context = '') {
  20. console.error(`[Bazaar Filler] ${context}:`, error);
  21.  
  22. if (error.userMessage) {
  23. alert(error.userMessage);
  24. }
  25. }
  26.  
  27. function safeExecute(fn, context = '') {
  28. return async function(...args) {
  29. try {
  30. return await fn.apply(this, args);
  31. } catch (error) {
  32. handleError(error, context);
  33. return null;
  34. }
  35. };
  36. }
  37.  
  38. const styleBlock = `
  39. .item-toggle {
  40. width: 18px;
  41. height: 18px;
  42. border-radius: 4px;
  43. -webkit-appearance: none;
  44. -moz-appearance: none;
  45. appearance: none;
  46. outline: none;
  47. }
  48. @keyframes spin {
  49. from { transform: rotate(0deg); }
  50. to { transform: rotate(360deg); }
  51. }
  52. .item-toggle::after {
  53. content: '\\2713';
  54. position: absolute;
  55. font-size: 14px;
  56. top: 50%;
  57. left: 50%;
  58. transform: translate(-50%, -50%);
  59. display: none;
  60. }
  61. .item-toggle:checked::after {
  62. display: block;
  63. }
  64.  
  65. body:not(.dark-mode) .item-toggle {
  66. border: 1px solid #ccc;
  67. background: #fff;
  68. }
  69. body:not(.dark-mode) .item-toggle:checked {
  70. background: #007bff;
  71. }
  72. body:not(.dark-mode) .item-toggle:checked::after {
  73. color: #fff;
  74. }
  75.  
  76. body.dark-mode .item-toggle {
  77. border: 1px solid #4e535a;
  78. background: #2f3237;
  79. }
  80. body.dark-mode .item-toggle:checked {
  81. background: #4e535a;
  82. }
  83. body.dark-mode .item-toggle:checked::after {
  84. color: #fff;
  85. }
  86.  
  87. .checkbox-wrapper {
  88. position: absolute;
  89. top: 50%;
  90. right: 8px;
  91. width: 30px;
  92. height: 30px;
  93. transform: translateY(-50%);
  94. cursor: pointer;
  95. }
  96. .checkbox-wrapper input.item-toggle {
  97. position: absolute;
  98. top: 6px;
  99. left: 6px;
  100. }
  101.  
  102. .settings-modal-overlay {
  103. position: fixed;
  104. top: 0; left: 0;
  105. width: 100%; height: 100%;
  106. background: rgba(0,0,0,0.5);
  107. z-index: 9999;
  108. display: flex;
  109. align-items: center;
  110. justify-content: center;
  111. }
  112. .settings-modal {
  113. background: #fff;
  114. padding: 20px;
  115. border-radius: 8px;
  116. min-width: 300px;
  117. box-shadow: 0 2px 10px rgba(0,0,0,0.3);
  118. color: #000;
  119. }
  120. .settings-modal h2 {
  121. margin-top: 0;
  122. }
  123. .settings-modal label {
  124. display: block;
  125. margin: 10px 0 5px;
  126. }
  127. .settings-modal input, .settings-modal select {
  128. width: 100%;
  129. padding: 5px;
  130. box-sizing: border-box;
  131. }
  132. .settings-modal button {
  133. margin-top: 15px;
  134. padding: 5px 10px;
  135. }
  136. .settings-modal div[style*="text-align:right"] {
  137. text-align: right;
  138. }
  139. body.dark-mode .settings-modal {
  140. background: #2f3237;
  141. color: #fff;
  142. box-shadow: 0 2px 10px rgba(0,0,0,0.7);
  143. }
  144. body.dark-mode .settings-modal input,
  145. body.dark-mode .settings-modal select {
  146. background: #3c3f41;
  147. color: #fff;
  148. border: 1px solid #555;
  149. }
  150. body.dark-mode .settings-modal button {
  151. background: #555;
  152. color: #fff;
  153. border: none;
  154. }
  155.  
  156. .black-friday-active {
  157. color: #28a745 !important;
  158. }
  159. .black-friday-active .black-friday-icon {
  160. color: #28a745 !important;
  161. fill: #28a745 !important;
  162. }
  163. .black-friday-icon {
  164. color: inherit;
  165. fill: currentColor;
  166. }
  167. `;
  168. $("<style>")
  169. .prop("type", "text/css")
  170. .html(styleBlock)
  171. .appendTo("head");
  172.  
  173. let apiKey = GM_getValue("tornApiKey", "");
  174. let pricingSource = GM_getValue("pricingSource", "Market Value");
  175. if (pricingSource === "Bazaars/TornPal") {
  176. pricingSource = "Bazaars/weav3r.dev";
  177. GM_setValue("pricingSource", pricingSource);
  178. }
  179. let itemMarketOffset = GM_getValue("itemMarketOffset", -1);
  180. let itemMarketMarginType = GM_getValue("itemMarketMarginType", "absolute");
  181. let itemMarketListing = GM_getValue("itemMarketListing", 1);
  182. let itemMarketClamp = GM_getValue("itemMarketClamp", false);
  183. let marketMarginOffset = GM_getValue("marketMarginOffset", 0);
  184. let marketMarginType = GM_getValue("marketMarginType", "absolute");
  185. let bazaarMarginOffset = GM_getValue("bazaarMarginOffset", 0);
  186. let bazaarMarginType = GM_getValue("bazaarMarginType", "absolute");
  187. let bazaarClamp = GM_getValue("bazaarClamp", false);
  188. let bazaarListing = GM_getValue("bazaarListing", 1);
  189. let blackFridayMode = GM_getValue("blackFridayMode", false);
  190. const validPages = ["#/add", "#/manage"];
  191. let currentPage = window.location.hash;
  192. let itemMarketCache = {};
  193. let weav3rItemCache = {};
  194.  
  195.  
  196.  
  197. function getItemIdByName(itemName) {
  198. const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
  199. for (const [id, info] of Object.entries(storedItems)) {
  200. if (info.name === itemName)
  201. return id;
  202. }
  203. return null;
  204. }
  205. function getPriceColor(listedPrice, marketValue) {
  206. if (marketValue <= 0)
  207. return "";
  208. const ratio = listedPrice / marketValue;
  209. const lowerBound = 0.998;
  210. const upperBound = 1.002;
  211. const isDarkMode = document.body.classList.contains("dark-mode");
  212. if (ratio >= lowerBound && ratio <= upperBound) {
  213. return "";
  214. }
  215. if (ratio < lowerBound) {
  216. const diff = lowerBound - ratio;
  217. const t = Math.min(diff / 0.05, 1.2);
  218. if (isDarkMode) {
  219. const r = Math.round(255 - t * (255 - 190));
  220. const g = Math.round(255 - t * (255 - 70));
  221. const b = Math.round(255 - t * (255 - 70));
  222. return `rgb(${r},${g},${b})`;
  223. }
  224. else {
  225. const r = Math.round(180 - t * 40);
  226. const g = Math.round(60 - t * 40);
  227. const b = Math.round(60 - t * 40);
  228. return `rgb(${r},${g},${b})`;
  229. }
  230. }
  231. else {
  232. const diff = ratio - upperBound;
  233. const t = Math.min(diff / 0.05, 1.2);
  234. if (isDarkMode) {
  235. const r = Math.round(255 - t * (255 - 70));
  236. const g = Math.round(255 - t * (255 - 190));
  237. const b = Math.round(255 - t * (255 - 70));
  238. return `rgb(${r},${g},${b})`;
  239. }
  240. else {
  241. const r = Math.round(60 - t * 40);
  242. const g = Math.round(160 - t * 40);
  243. const b = Math.round(60 - t * 40);
  244. return `rgb(${r},${g},${b})`;
  245. }
  246. }
  247. }
  248. async function fetchItemMarketData(itemId) {
  249. if (!apiKey) {
  250. const error = new Error("No API key set for Item Market calls.");
  251. error.userMessage = "No API key set. Please set your Torn API key in Bazaar Filler Settings before continuing.";
  252. throw error;
  253. }
  254. const now = Date.now();
  255. if (itemMarketCache[itemId] && now - itemMarketCache[itemId].time < 30000) {
  256. return itemMarketCache[itemId].data;
  257. }
  258. const url = `https://api.torn.com/v2/market/${itemId}/itemmarket?comment=wBazaarFiller`;
  259. const res = await fetch(url, {
  260. headers: { Authorization: "ApiKey " + apiKey },
  261. });
  262. const data = await res.json();
  263. if (data.error) {
  264. const error = new Error("Item Market API error: " + data.error.error);
  265. error.userMessage = "Item Market API error: " + data.error.error;
  266. throw error;
  267. }
  268. itemMarketCache[itemId] = { time: now, data };
  269. return data;
  270. }
  271. async function fetchWeav3rItemData(itemId) {
  272. const now = Date.now();
  273. if (weav3rItemCache[itemId] && now - weav3rItemCache[itemId].time < 60000) {
  274. return weav3rItemCache[itemId].data;
  275. }
  276. return new Promise((resolve, reject) => {
  277. GM_xmlhttpRequest({
  278. method: "GET",
  279. url: `https://weav3r.dev/api/marketplace/${itemId}`,
  280. onload: function (response) {
  281. const data = JSON.parse(response.responseText);
  282. weav3rItemCache[itemId] = { time: now, data };
  283. resolve(data);
  284. },
  285. onerror: function (err) {
  286. reject(new Error("Failed fetching weav3r.dev item data"));
  287. },
  288. });
  289. });
  290. }
  291. function updatePriceFieldColor($priceInput) {
  292. var _a;
  293. let $row = $priceInput.closest("li.clearfix");
  294. let itemName = "";
  295. if ($row.length) {
  296. itemName = $row.find(".name-wrap span.t-overflow").text().trim();
  297. }
  298. else {
  299. $row = $priceInput.closest(".item___jLJcf");
  300. itemName = $row.length ? $row.find(".desc___VJSNQ b").text().trim() : "";
  301. }
  302. if (!itemName)
  303. return;
  304. const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
  305. const matchedItem = Object.values(storedItems).find((i) => i.name === itemName);
  306. if (!matchedItem || !matchedItem.market_value)
  307. return;
  308. const raw = ((_a = $priceInput.val()) === null || _a === void 0 ? void 0 : _a.replace(/,/g, "")) || "";
  309. const typedPrice = Number(raw);
  310. if (isNaN(typedPrice)) {
  311. $priceInput.css("color", "");
  312. return;
  313. }
  314. $priceInput.css("color", getPriceColor(typedPrice, matchedItem.market_value));
  315. }
  316. function attachPriceFieldObservers() {
  317. $(".price input").each(function () {
  318. const $el = $(this);
  319. if ($el.data("listenerAttached"))
  320. return;
  321. $el.on("input", function () {
  322. updatePriceFieldColor($(this));
  323. });
  324. $el.data("listenerAttached", true);
  325. updatePriceFieldColor($el);
  326. });
  327. $(".price___DoKP7 .input-money-group.success input.input-money").each(function () {
  328. const $el = $(this);
  329. if ($el.data("listenerAttached"))
  330. return;
  331. $el.on("input", function () {
  332. updatePriceFieldColor($(this));
  333. });
  334. $el.data("listenerAttached", true);
  335. updatePriceFieldColor($el);
  336. });
  337. $("[class*=bottomMobileMenu___] [class*=priceMobile___] .input-money-group.success input.input-money").each(function () {
  338. const $el = $(this);
  339. if ($el.data("listenerAttached"))
  340. return;
  341. $el.on("input", function () {
  342. updatePriceFieldColor($(this));
  343. });
  344. $el.data("listenerAttached", true);
  345. updatePriceFieldColor($el);
  346. });
  347. }
  348.  
  349. async function calculatePrice(itemName, itemId, matchedItem) {
  350. if (!matchedItem) return null;
  351.  
  352. if (pricingSource === "Market Value") {
  353. const mv = matchedItem.market_value;
  354. let finalPrice = mv;
  355. if (marketMarginType === "absolute") {
  356. finalPrice += marketMarginOffset;
  357. } else if (marketMarginType === "percentage") {
  358. finalPrice = Math.round(mv * (1 + marketMarginOffset / 100));
  359. }
  360. return { price: finalPrice, marketValue: mv };
  361. }
  362.  
  363. if (pricingSource === "Item Market" && itemId) {
  364. const data = await safeExecute(fetchItemMarketData, 'Fetch Item Market Data')(itemId);
  365. if (!data || !data.itemmarket?.listings?.length) return null;
  366.  
  367. const listings = data.itemmarket.listings;
  368. const baseIndex = Math.min(itemMarketListing - 1, listings.length - 1);
  369. const listingPrice = listings[baseIndex].price;
  370.  
  371. let finalPrice;
  372. if (itemMarketMarginType === "absolute") {
  373. finalPrice = listingPrice + itemMarketOffset;
  374. } else if (itemMarketMarginType === "percentage") {
  375. finalPrice = Math.round(listingPrice * (1 + itemMarketOffset / 100));
  376. } else {
  377. finalPrice = listingPrice;
  378. }
  379.  
  380. if (itemMarketClamp && matchedItem.market_value) {
  381. finalPrice = Math.max(finalPrice, matchedItem.market_value);
  382. }
  383.  
  384. return {
  385. price: finalPrice,
  386. marketValue: matchedItem.market_value,
  387. listings: listings.slice(0, 5)
  388. };
  389. }
  390.  
  391. if (pricingSource === "Bazaars/weav3r.dev") {
  392. if (!itemId) return null;
  393.  
  394. const itemData = await safeExecute(fetchWeav3rItemData, 'Fetch weav3r.dev Item Data')(itemId);
  395. if (!itemData || !itemData.listings || itemData.listings.length === 0) return null;
  396.  
  397. const baseIndex = Math.min(bazaarListing - 1, itemData.listings.length - 1);
  398. const basePrice = itemData.listings[baseIndex].price;
  399.  
  400. let finalPrice;
  401. if (bazaarMarginType === "absolute") {
  402. finalPrice = basePrice + bazaarMarginOffset;
  403. } else if (bazaarMarginType === "percentage") {
  404. finalPrice = Math.round(basePrice * (1 + bazaarMarginOffset / 100));
  405. } else {
  406. finalPrice = basePrice;
  407. }
  408.  
  409. if (bazaarClamp && matchedItem.market_value) {
  410. finalPrice = Math.max(finalPrice, matchedItem.market_value);
  411. }
  412.  
  413. return { price: finalPrice, marketValue: matchedItem.market_value };
  414. }
  415.  
  416. return null;
  417. }
  418.  
  419. async function updateAddRow($row, isChecked) {
  420. const $qtyInput = $row.find(".amount input").first();
  421. const $priceInput = $row.find(".price input").first();
  422. const $choiceCheckbox = $row.find("div.amount.choice-container input");
  423.  
  424. if (!isChecked) {
  425. if ($choiceCheckbox.length && $choiceCheckbox.prop("checked")) {
  426. $choiceCheckbox.click();
  427. }
  428. if ($qtyInput.data("orig") !== undefined) {
  429. $qtyInput.val($qtyInput.data("orig"));
  430. $qtyInput.removeData("orig");
  431. } else {
  432. $qtyInput.val("");
  433. }
  434. $qtyInput[0].dispatchEvent(new Event("keyup", { bubbles: true }));
  435. if ($priceInput.data("orig") !== undefined) {
  436. $priceInput.val($priceInput.data("orig"));
  437. $priceInput.removeData("orig");
  438. $priceInput.css("color", "");
  439. } else {
  440. $priceInput.val("");
  441. }
  442. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  443. $priceInput[0].dispatchEvent(new Event("keyup", { bubbles: true }));
  444. return;
  445. }
  446.  
  447. if (!$qtyInput.data("orig"))
  448. $qtyInput.data("orig", $qtyInput.val());
  449. if (!$priceInput.data("orig"))
  450. $priceInput.data("orig", $priceInput.val());
  451.  
  452. if ($choiceCheckbox.length) {
  453. if (!$choiceCheckbox.prop("checked")) {
  454. $choiceCheckbox.click();
  455. }
  456. } else {
  457. const qty = $row.find(".item-amount.qty").text().trim();
  458. $qtyInput.val(qty);
  459. $qtyInput[0].dispatchEvent(new Event("keyup", { bubbles: true }));
  460. }
  461.  
  462. if (blackFridayMode) {
  463. $priceInput.val("1");
  464. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  465. $priceInput[0].dispatchEvent(new Event("keyup", { bubbles: true }));
  466. return;
  467. }
  468.  
  469. const itemName = $row.find(".name-wrap span.t-overflow").text().trim();
  470. const itemId = getItemIdByName(itemName);
  471. const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
  472. const matchedItem = Object.values(storedItems).find((i) => i.name === itemName);
  473.  
  474. const priceData = await calculatePrice(itemName, itemId, matchedItem);
  475. if (!priceData) return;
  476.  
  477. if (priceData.listings) {
  478. const $checkbox = $row.find(".checkbox-wrapper input.item-toggle").first();
  479. const listingsText = priceData.listings
  480. .map((x, i) => `${i + 1}) $${x.price.toLocaleString("en-US")} x${x.amount}`)
  481. .join("\n");
  482. $checkbox.attr("title", listingsText);
  483. setTimeout(() => {
  484. $checkbox.removeAttr("title");
  485. }, 30000);
  486. }
  487.  
  488. $priceInput.val(priceData.price.toLocaleString("en-US"));
  489. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  490. $priceInput[0].dispatchEvent(new Event("keyup", { bubbles: true }));
  491.  
  492. if (priceData.marketValue) {
  493. $priceInput.css("color", getPriceColor(priceData.price, priceData.marketValue));
  494. }
  495. }
  496. async function updateManageRow($row, isChecked) {
  497. const $priceInput = $row.find(".price___DoKP7 .input-money-group.success input.input-money").first();
  498.  
  499. if ($priceInput.length === 0) {
  500. console.warn("Price input not found in the row:", $row);
  501. return;
  502. }
  503.  
  504. if (!isChecked) {
  505. if ($priceInput.data("orig") !== undefined) {
  506. $priceInput.val($priceInput.data("orig"));
  507. $priceInput.removeData("orig");
  508. $priceInput.css("color", "");
  509. } else {
  510. $priceInput.val("");
  511. }
  512. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  513. return;
  514. }
  515.  
  516. if (!$priceInput.data("orig"))
  517. $priceInput.data("orig", $priceInput.val());
  518.  
  519. if (blackFridayMode) {
  520. $priceInput.val("1");
  521. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  522. return;
  523. }
  524.  
  525. const itemName = $row.find(".desc___VJSNQ b").text().trim();
  526. const itemId = getItemIdByName(itemName);
  527. const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
  528. const matchedItem = Object.values(storedItems).find((i) => i.name === itemName);
  529.  
  530. const priceData = await calculatePrice(itemName, itemId, matchedItem);
  531. if (!priceData) return;
  532.  
  533. if (priceData.listings) {
  534. const $checkbox = $row.find(".checkbox-wrapper input.item-toggle").first();
  535. const listingsText = priceData.listings
  536. .map((x, i) => `${i + 1}) $${x.price.toLocaleString("en-US")} x${x.amount}`)
  537. .join("\n");
  538. $checkbox.attr("title", listingsText);
  539. setTimeout(() => {
  540. $checkbox.removeAttr("title");
  541. }, 30000);
  542. }
  543.  
  544. $priceInput.val(priceData.price.toLocaleString("en-US"));
  545. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  546.  
  547. if (priceData.marketValue) {
  548. $priceInput.css("color", getPriceColor(priceData.price, priceData.marketValue));
  549. }
  550. }
  551. async function updateManageRowMobile($row, isChecked) {
  552. const $priceInput = $row
  553. .find("[class*=bottomMobileMenu___] [class*=priceMobile___] .input-money-group.success input.input-money")
  554. .first();
  555.  
  556. if (!$priceInput.length) {
  557. console.error("Mobile price field not found.");
  558. return;
  559. }
  560.  
  561. if (!isChecked) {
  562. if ($priceInput.data("orig") !== undefined) {
  563. $priceInput.val($priceInput.data("orig"));
  564. $priceInput.removeData("orig");
  565. $priceInput.css("color", "");
  566. } else {
  567. $priceInput.val("");
  568. }
  569. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  570. return;
  571. }
  572.  
  573. if (!$priceInput.data("orig"))
  574. $priceInput.data("orig", $priceInput.val());
  575.  
  576. if (blackFridayMode) {
  577. $priceInput.val("1");
  578. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  579. return;
  580. }
  581.  
  582. const itemName = $row.find(".desc___VJSNQ b").text().trim();
  583. const itemId = getItemIdByName(itemName);
  584. const storedItems = JSON.parse(localStorage.getItem("tornItems") || "{}");
  585. const matchedItem = Object.values(storedItems).find((i) => i.name === itemName);
  586.  
  587. const priceData = await calculatePrice(itemName, itemId, matchedItem);
  588. if (!priceData) return;
  589.  
  590. $priceInput.val(priceData.price.toLocaleString("en-US"));
  591. $priceInput[0].dispatchEvent(new Event("input", { bubbles: true }));
  592.  
  593. if (priceData.marketValue) {
  594. $priceInput.css("color", getPriceColor(priceData.price, priceData.marketValue));
  595. }
  596. }
  597.  
  598. function openSettingsModal() {
  599. $(".settings-modal-overlay").remove();
  600. const $overlay = $('<div class="settings-modal-overlay"></div>');
  601. const $modal = $(`
  602. <div class="settings-modal" style="width:400px; max-width:90%; font-family:Arial, sans-serif;">
  603. <h2 style="margin-bottom:6px;">Bazaar Filler Settings</h2>
  604. <hr style="border-top:1px solid #ccc; margin:8px 0;">
  605. <div style="margin-bottom:15px;">
  606. <label for="api-key-input" style="font-weight:bold; display:block;">Torn API Key</label>
  607. <div style="display:flex; align-items:center; gap:8px;">
  608. <input id="api-key-input" type="text" placeholder="Enter API key" style="flex:1; padding:6px; box-sizing:border-box;" value="${apiKey || ''}">
  609. <button id="refresh-market-values" style="padding:6px; cursor:pointer; background:none; border:none;" title="Refresh Market Values">
  610. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  611. <path d="M23 4v6h-6"></path>
  612. <path d="M1 20v-6h6"></path>
  613. <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
  614. </svg>
  615. </button>
  616. </div>
  617. </div>
  618. <hr style="border-top:1px solid #ccc; margin:8px 0;">
  619. <div style="margin-bottom:15px;">
  620. <label for="pricing-source-select" style="font-weight:bold; display:block;">Pricing Source</label>
  621. <select id="pricing-source-select" style="width:100%; padding:6px; box-sizing:border-box;">
  622. <option value="Market Value">Market Value</option>
  623. <option value="Bazaars/weav3r.dev">Bazaars/weav3r.dev</option>
  624. <option value="Item Market">Item Market</option>
  625. </select>
  626. </div>
  627. <div id="market-value-options" style="display:none; margin-bottom:15px;">
  628. <hr style="border-top:1px solid #ccc; margin:8px 0;">
  629. <h3 style="margin:0 0 10px 0; font-size:1em; font-weight:bold;">Market Value Options</h3>
  630. <div style="margin-bottom:10px;">
  631. <label for="market-margin-offset" style="display:block;">Margin (ie: -1 is either $1 less or 1% less depending on margin type)</label>
  632. <input id="market-margin-offset" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${marketMarginOffset}">
  633. </div>
  634. <div style="margin-bottom:10px;">
  635. <label for="market-margin-type" style="display:block;">Margin Type</label>
  636. <select id="market-margin-type" style="width:100%; padding:6px; box-sizing:border-box;">
  637. <option value="absolute">Absolute ($)</option>
  638. <option value="percentage">Percentage (%)</option>
  639. </select>
  640. </div>
  641. </div>
  642. <div id="item-market-options" style="display:none; margin-bottom:15px;">
  643. <hr style="border-top:1px solid #ccc; margin:8px 0;">
  644. <h3 style="margin:0 0 10px 0; font-size:1em; font-weight:bold;">Item Market Options</h3>
  645. <div style="margin-bottom:10px;">
  646. <label for="item-market-listing" style="display:block;">Listing Index (1 = lowest, 2 = 2nd lowest, etc)</label>
  647. <input id="item-market-listing" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${itemMarketListing}">
  648. </div>
  649. <div style="margin-bottom:10px;">
  650. <label for="item-market-offset" style="display:block;">Margin (ie: -1 is either $1 less or 1% less depending on margin type)</label>
  651. <input id="item-market-offset" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${itemMarketOffset}">
  652. </div>
  653. <div style="margin-bottom:10px;">
  654. <label for="item-market-margin-type" style="display:block;">Margin Type</label>
  655. <select id="item-market-margin-type" style="width:100%; padding:6px; box-sizing:border-box;">
  656. <option value="absolute">Absolute ($)</option>
  657. <option value="percentage">Percentage (%)</option>
  658. </select>
  659. </div>
  660. <div style="display:inline-flex; align-items:center; margin-bottom:5px;">
  661. <input id="item-market-clamp" type="checkbox" style="margin-right:5px;" ${itemMarketClamp ? "checked" : ""}>
  662. <label for="item-market-clamp" style="margin:0; cursor:pointer;">Clamp minimum price to Market Value</label>
  663. </div>
  664. </div>
  665. <div id="weav3r-options" style="display:none; margin-bottom:15px;">
  666. <hr style="border-top:1px solid #ccc; margin:8px 0;">
  667. <h3 style="margin:0 0 10px 0; font-size:1em; font-weight:bold;">weav3r.dev Options</h3>
  668. <div style="margin-bottom:10px;">
  669. <label for="weav3r-listing" style="display:block;">Listing Index (1 = lowest, 2 = 2nd lowest, etc)</label>
  670. <input id="weav3r-listing" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${bazaarListing || 1}">
  671. </div>
  672. <div style="margin-bottom:10px;">
  673. <label for="weav3r-margin-offset" style="display:block;">Margin (e.g., -1 for $1 less or 1% less)</label>
  674. <input id="weav3r-margin-offset" type="number" style="width:100%; padding:6px; box-sizing:border-box;" value="${bazaarMarginOffset}">
  675. </div>
  676. <div style="margin-bottom:10px;">
  677. <label for="weav3r-margin-type" style="display:block;">Margin Type</label>
  678. <select id="weav3r-margin-type" style="width:100%; padding:6px; box-sizing:border-box;">
  679. <option value="absolute">Absolute ($)</option>
  680. <option value="percentage">Percentage (%)</option>
  681. </select>
  682. </div>
  683. <div style="display:inline-flex; align-items:center; margin-bottom:5px;">
  684. <input id="weav3r-clamp" type="checkbox" style="margin-right:5px;" ${bazaarClamp ? "checked" : ""}>
  685. <label for="weav3r-clamp" style="margin:0; cursor:pointer;">Clamp minimum price to Market Value</label>
  686. </div>
  687. </div>
  688. <hr style="border-top:1px solid #ccc; margin:8px 0;">
  689. <div style="text-align:right;">
  690. <button id="settings-save" style="margin-right:8px; padding:6px 10px; cursor:pointer;">Save</button>
  691. <button id="settings-cancel" style="padding:6px 10px; cursor:pointer;">Cancel</button>
  692. </div>
  693. </div>
  694. `);
  695. $overlay.append($modal);
  696. $("body").append($overlay);
  697. $("#pricing-source-select").val(pricingSource);
  698. $("#item-market-margin-type").val(itemMarketMarginType);
  699. $("#market-margin-type").val(marketMarginType);
  700. $("#weav3r-margin-type").val(bazaarMarginType);
  701. function toggleFields() {
  702. const src = $("#pricing-source-select").val();
  703. $("#market-value-options").toggle(src === "Market Value");
  704. $("#item-market-options").toggle(src === "Item Market");
  705. $("#weav3r-options").toggle(src === "Bazaars/weav3r.dev");
  706. }
  707. $("#pricing-source-select").change(toggleFields);
  708. toggleFields();
  709. $("#settings-save").click(function () {
  710. var _a;
  711. const oldPricingSource = pricingSource;
  712. apiKey = ((_a = $("#api-key-input").val()) === null || _a === void 0 ? void 0 : _a.trim()) || "";
  713. pricingSource = $("#pricing-source-select").val();
  714. if (oldPricingSource !== pricingSource) {
  715. itemMarketCache = {};
  716. weav3rItemCache = {};
  717. }
  718. if (pricingSource === "Bazaars/weav3r.dev") {
  719. bazaarMarginOffset = Number($("#weav3r-margin-offset").val() || 0);
  720. bazaarMarginType = $("#weav3r-margin-type").val();
  721. bazaarClamp = $("#weav3r-clamp").is(":checked");
  722. bazaarListing = Number($("#weav3r-listing").val() || 1);
  723.  
  724. GM_setValue("bazaarMarginOffset", bazaarMarginOffset);
  725. GM_setValue("bazaarMarginType", bazaarMarginType);
  726. GM_setValue("bazaarClamp", bazaarClamp);
  727. GM_setValue("bazaarListing", bazaarListing);
  728. }
  729. if (pricingSource === "Market Value") {
  730. marketMarginOffset = Number($("#market-margin-offset").val() || 0);
  731. marketMarginType = $("#market-margin-type").val();
  732. GM_setValue("marketMarginOffset", marketMarginOffset);
  733. GM_setValue("marketMarginType", marketMarginType);
  734. }
  735. if (pricingSource === "Item Market") {
  736. itemMarketListing = Number($("#item-market-listing").val() || 1);
  737. itemMarketOffset = Number($("#item-market-offset").val() || -1);
  738. itemMarketMarginType = $("#item-market-margin-type").val();
  739. itemMarketClamp = $("#item-market-clamp").is(":checked");
  740. GM_setValue("itemMarketListing", itemMarketListing);
  741. GM_setValue("itemMarketOffset", itemMarketOffset);
  742. GM_setValue("itemMarketMarginType", itemMarketMarginType);
  743. GM_setValue("itemMarketClamp", itemMarketClamp);
  744. }
  745. GM_setValue("tornApiKey", apiKey);
  746. GM_setValue("pricingSource", pricingSource);
  747. $overlay.remove();
  748. });
  749. $("#settings-cancel").click(() => $overlay.remove());
  750.  
  751. $("#refresh-market-values").click(safeExecute(async function() {
  752. const $button = $(this);
  753. const $svg = $button.find("svg");
  754. const currentApiKey = $("#api-key-input").val().trim() || apiKey;
  755.  
  756. if (!currentApiKey) {
  757. const error = new Error("No API key");
  758. error.userMessage = "Please enter a valid API key first.";
  759. throw error;
  760. }
  761.  
  762. $button.prop("disabled", true);
  763. $svg.css("animation", "spin 1s linear infinite");
  764.  
  765. try {
  766. const response = await fetch(`https://api.torn.com/torn/?key=${currentApiKey}&selections=items&comment=wBazaarFiller`);
  767. const data = await response.json();
  768.  
  769. if (!data.items) {
  770. throw new Error(data.error?.error || "Failed to fetch market values");
  771. }
  772.  
  773. const filtered = {};
  774. for (const [id, item] of Object.entries(data.items)) {
  775. if (item.tradeable) {
  776. filtered[id] = {
  777. name: item.name,
  778. market_value: item.market_value,
  779. };
  780. }
  781. }
  782.  
  783. localStorage.setItem("tornItems", JSON.stringify(filtered));
  784. GM_setValue("lastUpdatedTime", Date.now());
  785.  
  786. const $successMsg = $('<div style="color: #28a745; margin-top: 5px;">Market values refreshed successfully!</div>');
  787. $button.after($successMsg);
  788. setTimeout(() => $successMsg.remove(), 3000);
  789. } catch (error) {
  790. const $errorMsg = $('<div style="color: #dc3545; margin-top: 5px;">Error: ' + error.message + '</div>');
  791. $button.after($errorMsg);
  792. setTimeout(() => $errorMsg.remove(), 3000);
  793. throw error;
  794. } finally {
  795. $button.prop("disabled", false);
  796. $svg.css("animation", "");
  797. }
  798. }, 'Refresh Market Values'));
  799. }
  800. function addPricingSourceLink() {
  801. if (document.getElementById("pricing-source-button"))
  802. return;
  803.  
  804. const linksContainer = document.querySelector(".linksContainer___LiOTN");
  805. if (!linksContainer) {
  806. return;
  807. }
  808.  
  809. const link = document.createElement("a");
  810. link.id = "pricing-source-button";
  811. link.href = "#";
  812. link.className = "linkContainer___X16y4 inRow___VfDnd greyLineV___up8VP iconActive___oAum9";
  813. link.target = "_self";
  814. link.rel = "noreferrer";
  815.  
  816. const iconSpan = document.createElement("span");
  817. iconSpan.className = "iconWrapper___x3ZLe iconWrapper___COKJD svgIcon___IwbJV";
  818. iconSpan.innerHTML = `
  819. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
  820. <path d="M8 4.754a3.246 3.246 0 1 1 0 6.492 3.246 3.246 0 0 1 0-6.492zM5.754 8a2.246 2.246 0 1 0 4.492 0 2.246 2.246 0 0 0-4.492 0z"/>
  821. <path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 0-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 0-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 0 .52 1.255l-.16.292c-.892 1.64.901 3.433 2.54 2.54l.292-.16a.873.873 0 0 0 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 0 1.255-.52l.292.16c1.64.893 3.433-.902 2.54-2.541l-.16-.292a.873.873 0 0 0-.52-1.255l-.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 0-.52-1.255l-.16-.292c-.893-1.64-.902-3.433-2.54-2.54l-.292.16a.873.873 0 0 0-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.416 1.6.42 1.184 1.185l-.16.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.318.094a1.873 1.873 0 0 0-1.116 2.692l.16.292c.416.764-.42 1.6-1.185 1.184l-.291-.16a1.873 1.873 0 0 0-1.116-2.692l-.318-.094c-.835-.246-.835-1.428 0-1.674l.318-.094a1.873 1.873 0 0 0 1.116-2.692l-.16-.292c-.416-.764.42-1.6 1.185-1.184l.292.16a1.873 1.873 0 0 0 2.693-1.115l.094-.318z"/>
  822. </svg>
  823. `;
  824. link.appendChild(iconSpan);
  825.  
  826. const textSpan = document.createElement("span");
  827. textSpan.className = "linkTitle____NPyM";
  828. textSpan.textContent = "Bazaar Filler Settings";
  829. link.appendChild(textSpan);
  830.  
  831. link.addEventListener("click", function (e) {
  832. e.preventDefault();
  833. openSettingsModal();
  834. });
  835.  
  836. linksContainer.insertBefore(link, linksContainer.firstChild);
  837. }
  838. function addBlackFridayToggle() {
  839. if (document.getElementById("black-friday-toggle"))
  840. return;
  841.  
  842. const linksContainer = document.querySelector(".linksContainer___LiOTN");
  843. if (!linksContainer) {
  844. return;
  845. }
  846.  
  847. const link = document.createElement("a");
  848. link.id = "black-friday-toggle";
  849. link.href = "#";
  850. link.className = "linkContainer___X16y4 inRow___VfDnd greyLineV___up8VP iconActive___oAum9";
  851. if (blackFridayMode) {
  852. link.classList.add("black-friday-active");
  853. }
  854. link.target = "_self";
  855. link.rel = "noreferrer";
  856.  
  857. const iconSpan = document.createElement("span");
  858. iconSpan.className = "iconWrapper___x3ZLe iconWrapper___COKJD svgIcon___IwbJV";
  859. iconSpan.innerHTML = `
  860. <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" class="black-friday-icon" style="color: ${blackFridayMode ? "#28a745" : "inherit"}; fill: ${blackFridayMode ? "#28a745" : "currentColor"};">
  861. <path d="M4 10.781c.148 1.667 1.513 2.85 3.591 3.003V15h1.043v-1.216c2.27-.179 3.678-1.438 3.678-3.3 0-1.59-.947-2.51-2.956-3.028l-.722-.187V3.467c1.122.11 1.879.714 2.07 1.616h1.47c-.166-1.6-1.54-2.748-3.54-2.875V1H7.591v1.233c-1.939.23-3.27 1.472-3.27 3.156 0 1.454.966 2.483 2.661 2.917l.61.162v4.031c-1.149-.17-1.94-.8-2.131-1.718H4zm3.391-3.836c-1.043-.263-1.6-.825-1.6-1.616 0-.944.704-1.641 1.8-1.828v3.495l-.2-.05zm1.591 1.872c1.287.323 1.852.859 1.852 1.769 0 1.097-.826 1.828-2.2 1.939V8.73l.348.086z"/>
  862. </svg>
  863. `;
  864. link.appendChild(iconSpan);
  865.  
  866. const textSpan = document.createElement("span");
  867. textSpan.className = "linkTitle____NPyM";
  868. textSpan.textContent = blackFridayMode ? "Black Friday: ON" : "Black Friday: OFF";
  869. link.appendChild(textSpan);
  870.  
  871. link.addEventListener("click", function (e) {
  872. e.preventDefault();
  873. blackFridayMode = !blackFridayMode;
  874. GM_setValue("blackFridayMode", blackFridayMode);
  875. textSpan.textContent = blackFridayMode ? "Black Friday: ON" : "Black Friday: OFF";
  876. const svg = this.querySelector(".black-friday-icon");
  877. if (svg) {
  878. svg.style.color = blackFridayMode ? "#28a745" : "inherit";
  879. svg.style.fill = blackFridayMode ? "#28a745" : "currentColor";
  880. }
  881. if (blackFridayMode) {
  882. link.classList.add("black-friday-active");
  883. }
  884. else {
  885. link.classList.remove("black-friday-active");
  886. }
  887. });
  888.  
  889. const settingsButton = document.getElementById("pricing-source-button");
  890. if (settingsButton) {
  891. linksContainer.insertBefore(link, settingsButton);
  892. }
  893. else {
  894. linksContainer.insertBefore(link, linksContainer.firstChild);
  895. }
  896. }
  897. function createItemToggleCheckbox(updateFunction, context) {
  898. return $("<input>", {
  899. type: "checkbox",
  900. class: "item-toggle",
  901. click: safeExecute(async function (e) {
  902. e.stopPropagation();
  903. if (!GM_getValue("tornApiKey", "")) {
  904. const error = new Error("No API key set");
  905. error.userMessage = "No Torn API key set. Please click the 'Bazaar Filler Settings' button to enter your API key.";
  906. $(this).prop("checked", false);
  907. openSettingsModal();
  908. throw error;
  909. }
  910. await updateFunction.call(this, e);
  911. }, context),
  912. });
  913. }
  914.  
  915. function addAddPageCheckboxes() {
  916. $(".items-cont .title-wrap").each(function () {
  917. const $el = $(this);
  918. if ($el.find(".checkbox-wrapper").length)
  919. return;
  920. $el.css("position", "relative");
  921. const wrapper = $('<div class="checkbox-wrapper"></div>');
  922. const checkbox = createItemToggleCheckbox(async function(e) {
  923. await updateAddRow($(this).closest("li.clearfix"), this.checked);
  924. }, 'Add Page Checkbox Click');
  925. wrapper.append(checkbox);
  926. $el.append(wrapper);
  927. });
  928. $(document)
  929. .off("dblclick", ".amount input")
  930. .on("dblclick", ".amount input", function () {
  931. const $row = $(this).closest("li.clearfix");
  932. const qty = $row.find(".item-amount.qty").text().trim();
  933. if (qty) {
  934. $(this).val(qty);
  935. $(this)[0].dispatchEvent(new Event("input", { bubbles: true }));
  936. $(this)[0].dispatchEvent(new Event("keyup", { bubbles: true }));
  937. }
  938. });
  939.  
  940. if ($(".select-all-action").length === 0) {
  941. const $clearAllBtn = $(".clear-action");
  942. if ($clearAllBtn.length) {
  943. const $selectAllBtn = $('<span class="select-all-action t-blue h c-pointer" style="margin-left: 15px;">Select All</span>');
  944. $clearAllBtn.before($selectAllBtn);
  945.  
  946. $selectAllBtn.on("click", safeExecute(async function(e) {
  947. e.preventDefault();
  948. if (!GM_getValue("tornApiKey", "")) {
  949. const error = new Error("No API key set");
  950. error.userMessage = "No Torn API key set. Please click the 'Bazaar Filler Settings' button to enter your API key.";
  951. openSettingsModal();
  952. throw error;
  953. }
  954.  
  955. let $activePanel = $(".items-cont.ui-tabs-panel[style*='display: block']");
  956. if (!$activePanel.length) {
  957. const $activeTab = $(".ui-tabs-active.ui-state-active");
  958. if ($activeTab.length) {
  959. const tabId = $activeTab.find("a").attr("href").replace("#", "");
  960. $activePanel = $(`.items-cont.ui-tabs-panel[data-reactid*='$${tabId}']`);
  961. }
  962. if (!$activePanel.length) {
  963. $activePanel = $(".items-cont.ui-tabs-panel").filter(function() {
  964. return $(this).css("display") !== "none";
  965. });
  966. }
  967. }
  968.  
  969. if ($activePanel.length) {
  970. const $checkboxes = $activePanel.find("li.clearfix:not(.disabled) .checkbox-wrapper input.item-toggle:not(:checked)");
  971. if ($checkboxes.length === 0) return;
  972.  
  973. for (let i = 0; i < $checkboxes.length; i++) {
  974. const $checkbox = $($checkboxes[i]);
  975. $checkbox.prop("checked", true);
  976. const $row = $checkbox.closest("li.clearfix");
  977. await updateAddRow($row, true);
  978. }
  979. }
  980. }, 'Select All Click'));
  981. }
  982. }
  983. }
  984. function addManagePageCheckboxes() {
  985. $(".item___jLJcf").each(function () {
  986. const $row = $(this);
  987. const $desc = $row.find(".desc___VJSNQ");
  988. if (!$desc.length || $desc.find(".checkbox-wrapper").length)
  989. return;
  990. $desc.css("position", "relative");
  991. const wrapper = $('<div class="checkbox-wrapper"></div>');
  992. const checkbox = createItemToggleCheckbox(async function(e) {
  993. const $row = $(this).closest(".item___jLJcf");
  994. if ($row.length === 0) {
  995. const $correctRow = $(this).closest(".item___jLJcf");
  996. if ($correctRow.length > 0) {
  997. if (window.innerWidth <= 784) {
  998. const $manageBtn = $correctRow.find('button[aria-label="Manage"]').first();
  999. if ($manageBtn.length) {
  1000. if (!$manageBtn.find("span").hasClass("active___OTFsm")) {
  1001. $manageBtn.click();
  1002. }
  1003. setTimeout(async () => {
  1004. await updateManageRowMobile($correctRow, this.checked);
  1005. }, 200);
  1006. return;
  1007. }
  1008. }
  1009. await updateManageRow($correctRow, this.checked);
  1010. return;
  1011. }
  1012. console.warn("Row not found with either selector");
  1013. return;
  1014. }
  1015. await updateManageRow($row, this.checked);
  1016. }, 'Manage Page Checkbox Click');
  1017. wrapper.append(checkbox);
  1018. $desc.append(wrapper);
  1019. });
  1020. }
  1021.  
  1022. const storedItems = localStorage.getItem("tornItems");
  1023. const lastUpdatedTime = GM_getValue("lastUpdatedTime", 0);
  1024. const now = Date.now();
  1025. const oneDayMs = 24 * 60 * 60 * 1000;
  1026. const lastUpdatedDate = new Date(lastUpdatedTime);
  1027. const todayUTC = new Date().toISOString().split("T")[0];
  1028. const lastUpdatedUTC = lastUpdatedDate.toISOString().split("T")[0];
  1029.  
  1030. if (apiKey && (!storedItems || lastUpdatedUTC < todayUTC || now - lastUpdatedTime >= oneDayMs)) {
  1031. safeExecute(async () => {
  1032. const response = await fetch(`https://api.torn.com/torn/?key=${apiKey}&selections=items&comment=wBazaarFiller`);
  1033. const data = await response.json();
  1034.  
  1035. if (!data.items) {
  1036. throw new Error("Failed to fetch Torn items or no items found. Possibly invalid API key or rate limit.");
  1037. }
  1038.  
  1039. const filtered = {};
  1040. for (const [id, item] of Object.entries(data.items)) {
  1041. if (item.tradeable) {
  1042. filtered[id] = {
  1043. name: item.name,
  1044. market_value: item.market_value,
  1045. };
  1046. }
  1047. }
  1048.  
  1049. localStorage.setItem("tornItems", JSON.stringify(filtered));
  1050. GM_setValue("lastUpdatedTime", now);
  1051. }, 'Initial Item Fetch')();
  1052. }
  1053. let observerTimeout;
  1054. const domObserver = new MutationObserver((mutations) => {
  1055. clearTimeout(observerTimeout);
  1056. observerTimeout = setTimeout(() => {
  1057. safeExecute(() => {
  1058. const hash = window.location.hash;
  1059. if (hash === "#/add") {
  1060. addAddPageCheckboxes();
  1061. }
  1062. else if (hash === "#/manage") {
  1063. addManagePageCheckboxes();
  1064. }
  1065. addPricingSourceLink();
  1066. addBlackFridayToggle();
  1067. attachPriceFieldObservers();
  1068. }, 'DOM Observer')();
  1069. }, 100);
  1070. });
  1071.  
  1072. const observeTarget = document.querySelector('#bazaarRoot') || document.body;
  1073. domObserver.observe(observeTarget, {
  1074. childList: true,
  1075. subtree: true
  1076. });
  1077.  
  1078. const initializeUI = safeExecute(() => {
  1079. const hash = window.location.hash;
  1080. if (hash === "#/add") {
  1081. addAddPageCheckboxes();
  1082. } else if (hash === "#/manage") {
  1083. addManagePageCheckboxes();
  1084. }
  1085. addPricingSourceLink();
  1086. addBlackFridayToggle();
  1087. attachPriceFieldObservers();
  1088. }, 'Initialize UI');
  1089.  
  1090. window.addEventListener('load', () => setTimeout(initializeUI, 100));
  1091. window.addEventListener("hashchange", () => {
  1092. currentPage = window.location.hash;
  1093. setTimeout(initializeUI, 100);
  1094. });
  1095.  
  1096. $(document).on("click", "button.undo___FTgvP", function (e) {
  1097. e.preventDefault();
  1098. $(".item___jLJcf .checkbox-wrapper input.item-toggle:checked").each(function () {
  1099. $(this).prop("checked", false);
  1100. const $row = $(this).closest(".item___jLJcf");
  1101. updateManageRow($row, false);
  1102. });
  1103. });
  1104. $(document).on("click", ".clear-action", function (e) {
  1105. e.preventDefault();
  1106. $("li.clearfix .checkbox-wrapper input.item-toggle:checked").each(function () {
  1107. $(this).prop("checked", false);
  1108. const $row = $(this).closest("li.clearfix");
  1109. updateAddRow($row, false);
  1110. });
  1111. });
  1112. $(document).ready(function () {
  1113. itemMarketCache = {};
  1114. weav3rItemCache = {};
  1115. });
  1116. })();

QingJ © 2025

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