Advertisement
Guest User

Untitled

a guest
Aug 24th, 2017
1,302
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 128.58 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Steam Economy Enhancer
  3. // @namespace https://github.com/Nuklon
  4. // @author Nuklon
  5. // @license MIT
  6. // @version 5.8.0
  7. // @description Enhances the Steam Inventory and Steam Market.
  8. // @include *://steamcommunity.com/id/*/inventory*
  9. // @include *://steamcommunity.com/profiles/*/inventory*
  10. // @include *://steamcommunity.com/market*
  11. // @include *://steamcommunity.com/tradeoffer*
  12. // @require https://code.jquery.com/jquery-3.2.1.min.js
  13. // @require https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
  14. // @require https://raw.githubusercontent.com/kapetan/jquery-observe/master/jquery-observe.js
  15. // @require https://raw.githubusercontent.com/superRaytin/paginationjs/master/dist/pagination.js
  16. // @require https://raw.githubusercontent.com/caolan/async/master/dist/async.min.js
  17. // @require https://cdnjs.cloudflare.com/ajax/libs/localforage/1.4.3/localforage.min.js
  18. // @require https://cdnjs.cloudflare.com/ajax/libs/datejs/1.0/date.min.js
  19. // @require https://raw.githubusercontent.com/javve/list.js/v1.5.0/dist/list.min.js
  20. // @require https://github.com/rmariuzzo/checkboxes.js/releases/download/v1.2.0/jquery.checkboxes-1.2.0.min.js
  21. // @homepageURL https://github.com/Nuklon/Steam-Economy-Enhancer
  22. // @supportURL https://github.com/Nuklon/Steam-Economy-Enhancer/issues
  23. // @downloadURL https://raw.githubusercontent.com/Nuklon/Steam-Economy-Enhancer/master/code.user.js
  24. // @updateURL https://raw.githubusercontent.com/Nuklon/Steam-Economy-Enhancer/master/code.user.js
  25. // ==/UserScript==
  26.  
  27. // jQuery is already added by Steam, force no conflict mode.
  28. (function ($, async) {
  29. $.noConflict(true);
  30.  
  31. const STEAM_INVENTORY_ID = 753;
  32.  
  33. const PAGE_MARKET = 0;
  34. const PAGE_MARKET_LISTING = 1;
  35. const PAGE_TRADEOFFER = 2;
  36. const PAGE_INVENTORY = 3;
  37.  
  38. const COLOR_ERROR = '#8A4243';
  39. const COLOR_SUCCESS = '#407736';
  40. const COLOR_PENDING = '#908F44';
  41. const COLOR_PRICE_FAIR = '#496424';
  42. const COLOR_PRICE_CHEAP = '#837433';
  43. const COLOR_PRICE_EXPENSIVE = '#813030';
  44.  
  45. const ERROR_SUCCESS = null;
  46. const ERROR_FAILED = 1;
  47. const ERROR_DATA = 2;
  48.  
  49. var marketLists = [];
  50. var totalNumberOfProcessedQueueItems = 0;
  51. var totalNumberOfQueuedItems = 0;
  52.  
  53. var spinnerBlock =
  54. '<div class="spinner"><div class="rect1"></div>&nbsp;<div class="rect2"></div>&nbsp;<div class="rect3"></div>&nbsp;<div class="rect4"></div>&nbsp;<div class="rect5"></div>&nbsp;</div>';
  55. var numberOfFailedRequests = 0;
  56.  
  57. var enableConsoleLog = false;
  58.  
  59. var isLoggedIn = typeof g_rgWalletInfo !== 'undefined' && g_rgWalletInfo != null || (typeof g_bLoggedIn !== 'undefined' && g_bLoggedIn);
  60.  
  61. var currentPage = window.location.href.includes('.com/market')
  62. ? (window.location.href.includes('market/listings')
  63. ? PAGE_MARKET_LISTING
  64. : PAGE_MARKET)
  65. : (window.location.href.includes('.com/tradeoffer')
  66. ? PAGE_TRADEOFFER
  67. : PAGE_INVENTORY);
  68.  
  69. var market = new SteamMarket(g_rgAppContextData,
  70. typeof g_strInventoryLoadURL !== 'undefined' && g_strInventoryLoadURL != null
  71. ? g_strInventoryLoadURL
  72. : location.protocol + '//steamcommunity.com/my/inventory/json/',
  73. isLoggedIn ? g_rgWalletInfo : undefined);
  74.  
  75. var currencyId =
  76. isLoggedIn &&
  77. market != null &&
  78. market.walletInfo != null &&
  79. market.walletInfo.wallet_currency != null
  80. ? market.walletInfo.wallet_currency
  81. : 3;
  82.  
  83. var currencySymbol = GetCurrencySymbol(GetCurrencyCode(currencyId));
  84.  
  85. function SteamMarket(appContext, inventoryUrl, walletInfo) {
  86. this.appContext = appContext;
  87. this.inventoryUrl = inventoryUrl;
  88. this.walletInfo = walletInfo;
  89. this.inventoryUrlBase = inventoryUrl.replace('/inventory/json', '');
  90. if (!this.inventoryUrlBase.endsWith('/'))
  91. this.inventoryUrlBase += '/';
  92. }
  93.  
  94. //#region Settings
  95. const SETTING_MIN_NORMAL_PRICE = 'SETTING_MIN_NORMAL_PRICE';
  96. const SETTING_MAX_NORMAL_PRICE = 'SETTING_MAX_NORMAL_PRICE';
  97. const SETTING_MIN_FOIL_PRICE = 'SETTING_MIN_FOIL_PRICE';
  98. const SETTING_MAX_FOIL_PRICE = 'SETTING_MAX_FOIL_PRICE';
  99. const SETTING_MIN_MISC_PRICE = 'SETTING_MIN_MISC_PRICE';
  100. const SETTING_MAX_MISC_PRICE = 'SETTING_MAX_MISC_PRICE';
  101. const SETTING_PRICE_OFFSET = 'SETTING_PRICE_OFFSET';
  102. const SETTING_PRICE_ALGORITHM = 'SETTING_PRICE_ALGORITHM';
  103. const SETTING_PRICE_IGNORE_LOWEST_Q = 'SETTING_PRICE_IGNORE_LOWEST_Q';
  104. const SETTING_LAST_CACHE = 'SETTING_LAST_CACHE';
  105. const SETTING_RELIST_AUTOMATICALLY = 'SETTING_RELIST_AUTOMATICALLY';
  106. const SETTING_MARKET_PAGE_COUNT = 'SETTING_MARKET_PAGE_COUNT';
  107.  
  108. var settingDefaults =
  109. {
  110. SETTING_MIN_NORMAL_PRICE: 0.05,
  111. SETTING_MAX_NORMAL_PRICE: 2.50,
  112. SETTING_MIN_FOIL_PRICE: 0.15,
  113. SETTING_MAX_FOIL_PRICE: 10,
  114. SETTING_MIN_MISC_PRICE: 0.05,
  115. SETTING_MAX_MISC_PRICE: 10,
  116. SETTING_PRICE_OFFSET: -0.01,
  117. SETTING_PRICE_ALGORITHM: 1,
  118. SETTING_PRICE_IGNORE_LOWEST_Q: 1,
  119. SETTING_LAST_CACHE: 0,
  120. SETTING_RELIST_AUTOMATICALLY: 0,
  121. SETTING_MARKET_PAGE_COUNT: 100
  122. };
  123.  
  124. function getSettingWithDefault(name) {
  125. return getLocalStorageItem(name) || (name in settingDefaults ? settingDefaults[name] : null);
  126. }
  127.  
  128. function setSetting(name, value) {
  129. setLocalStorageItem(name, value);
  130. }
  131. //#endregion
  132.  
  133. //#region Storage
  134.  
  135. var storagePersistent = localforage.createInstance({
  136. name: 'see_persistent'
  137. });
  138.  
  139. var storageSession;
  140.  
  141. var currentUrl = new URL(window.location.href);
  142. var noCache = currentUrl.searchParams.get('no-cache') != null;
  143.  
  144. // This does not work the same as the 'normal' session storage because opening a new browser session/tab will clear the cache.
  145. // For this reason, a rolling cache is used.
  146. if (getSessionStorageItem('SESSION') == null || noCache) {
  147. var lastCache = getSettingWithDefault(SETTING_LAST_CACHE);
  148. if (lastCache > 5)
  149. lastCache = 0;
  150.  
  151. setSetting(SETTING_LAST_CACHE, lastCache + 1);
  152.  
  153. storageSession = localforage.createInstance({
  154. name: 'see_session_' + lastCache
  155. });
  156.  
  157. storageSession.clear(); // Clear any previous data.
  158. setSessionStorageItem('SESSION', lastCache);
  159. } else {
  160. storageSession = localforage.createInstance({
  161. name: 'see_session_' + getSessionStorageItem('SESSION')
  162. });
  163. }
  164.  
  165. function getLocalStorageItem(name) {
  166. try {
  167. return localStorage.getItem(name);
  168. } catch (e) {
  169. return null;
  170. }
  171. }
  172.  
  173. function setLocalStorageItem(name, value) {
  174. try {
  175. localStorage.setItem(name, value);
  176. return true;
  177. } catch (e) {
  178. logConsole('Failed to set local storage item ' + name + ', ' + e + '.')
  179. return false;
  180. }
  181. }
  182.  
  183. function getSessionStorageItem(name) {
  184. try {
  185. return sessionStorage.getItem(name);
  186. } catch (e) {
  187. return null;
  188. }
  189. }
  190.  
  191. function setSessionStorageItem(name, value) {
  192. try {
  193. sessionStorage.setItem(name, value);
  194. return true;
  195. } catch (e) {
  196. logConsole('Failed to set session storage item ' + name + ', ' + e + '.')
  197. return false;
  198. }
  199. }
  200. //#endregion
  201.  
  202. //#region Price helpers
  203. function getPriceInformationFromItem(item) {
  204. var isTradingCard = getIsTradingCard(item);
  205. var isFoilTradingCard = getIsFoilTradingCard(item);
  206. return getPriceInformation(isTradingCard, isFoilTradingCard);
  207. }
  208.  
  209. function getPriceInformation(isTradingCard, isFoilTradingCard) {
  210. var maxPrice = 0;
  211. var minPrice = 0;
  212.  
  213. if (!isTradingCard) {
  214. maxPrice = getSettingWithDefault(SETTING_MAX_MISC_PRICE);
  215. minPrice = getSettingWithDefault(SETTING_MIN_MISC_PRICE);
  216. } else {
  217. maxPrice = isFoilTradingCard
  218. ? getSettingWithDefault(SETTING_MAX_FOIL_PRICE)
  219. : getSettingWithDefault(SETTING_MAX_NORMAL_PRICE);
  220. minPrice = isFoilTradingCard
  221. ? getSettingWithDefault(SETTING_MIN_FOIL_PRICE)
  222. : getSettingWithDefault(SETTING_MIN_NORMAL_PRICE);
  223. }
  224.  
  225. maxPrice = maxPrice * 100.0;
  226. minPrice = minPrice * 100.0;
  227.  
  228. var maxPriceBeforeFees = market.getPriceBeforeFees(maxPrice);
  229. var minPriceBeforeFees = market.getPriceBeforeFees(minPrice);
  230.  
  231. return { maxPrice, minPrice, maxPriceBeforeFees, minPriceBeforeFees };
  232. }
  233.  
  234. // Calculates the average history price, before the fee.
  235. function calculateAverageHistoryPriceBeforeFees(history) {
  236. var highest = 0;
  237. var total = 0;
  238.  
  239. if (history != null) {
  240. // Highest average price in the last 12 hours.
  241. var timeAgo = Date.now() - (12 * 60 * 60 * 1000);
  242.  
  243. history.forEach(function (historyItem) {
  244. var d = new Date(historyItem[0]);
  245. if (d.getTime() > timeAgo) {
  246. highest += historyItem[1] * historyItem[2];
  247. total += historyItem[2];
  248. }
  249. });
  250. }
  251.  
  252. if (total == 0)
  253. return 0;
  254.  
  255. highest = Math.floor(highest / total);
  256. return market.getPriceBeforeFees(highest);
  257. }
  258.  
  259. // Calculates the listing price, before the fee.
  260. function calculateListingPriceBeforeFees(histogram) {
  261. if (histogram == null ||
  262. histogram.lowest_sell_order == null ||
  263. histogram.sell_order_graph == null)
  264. return 0;
  265.  
  266. var listingPrice = market.getPriceBeforeFees(histogram.lowest_sell_order);
  267.  
  268. var shouldIgnoreLowestListingOnLowQuantity = getSettingWithDefault(SETTING_PRICE_IGNORE_LOWEST_Q) == 1;
  269.  
  270. if (shouldIgnoreLowestListingOnLowQuantity && histogram.sell_order_graph.length >= 2) {
  271. var listingPrice2ndLowest = market.getPriceBeforeFees(histogram.sell_order_graph[1][0] * 100);
  272.  
  273. if (listingPrice2ndLowest > listingPrice) {
  274. var numberOfListingsLowest = histogram.sell_order_graph[0][1];
  275. var numberOfListings2ndLowest = histogram.sell_order_graph[1][1];
  276.  
  277. var percentageLower = (100 * (numberOfListingsLowest / numberOfListings2ndLowest));
  278.  
  279. // The percentage should change based on the quantity (for example, 1200 listings vs 5, or 1 vs 25).
  280. if (numberOfListings2ndLowest >= 1000 && percentageLower <= 5) {
  281. listingPrice = listingPrice2ndLowest;
  282. } else if (numberOfListings2ndLowest < 1000 && percentageLower <= 10) {
  283. listingPrice = listingPrice2ndLowest;
  284. } else if (numberOfListings2ndLowest < 100 && percentageLower <= 15) {
  285. listingPrice = listingPrice2ndLowest;
  286. } else if (numberOfListings2ndLowest < 50 && percentageLower <= 20) {
  287. listingPrice = listingPrice2ndLowest;
  288. } else if (numberOfListings2ndLowest < 25 && percentageLower <= 25) {
  289. listingPrice = listingPrice2ndLowest;
  290. } else if (numberOfListings2ndLowest < 10 && percentageLower <= 30) {
  291. listingPrice = listingPrice2ndLowest;
  292. }
  293. }
  294. }
  295.  
  296. return listingPrice;
  297. }
  298.  
  299. // Calculate the sell price based on the history and listings.
  300. // applyOffset specifies whether the price offset should be applied when the listings are used to determine the price.
  301. function calculateSellPriceBeforeFees(history, histogram, applyOffset, minPriceBeforeFees, maxPriceBeforeFees) {
  302. var historyPrice = calculateAverageHistoryPriceBeforeFees(history);
  303. var listingPrice = calculateListingPriceBeforeFees(histogram);
  304.  
  305. var shouldUseAverage = getSettingWithDefault(SETTING_PRICE_ALGORITHM) == 1;
  306.  
  307. // If the highest average price is lower than the first listing, return the offset + that listing.
  308. // Otherwise, use the highest average price instead.
  309. var calculatedPrice = 0;
  310.  
  311. if (historyPrice < listingPrice || !shouldUseAverage) {
  312. calculatedPrice = listingPrice;
  313. } else {
  314. calculatedPrice = historyPrice;
  315. }
  316.  
  317. var changedToMax = false;
  318. // List for the maximum price if there are no listings yet.
  319. if (calculatedPrice == 0) {
  320. calculatedPrice = maxPriceBeforeFees;
  321. changedToMax = true;
  322. }
  323.  
  324.  
  325. // Apply the offset to the calculated price, but only if the price wasn't changed to the max (as otherwise it's impossible to list for this price).
  326. if (!changedToMax && applyOffset) {
  327. calculatedPrice = calculatedPrice + (getSettingWithDefault(SETTING_PRICE_OFFSET) * 100);
  328. }
  329.  
  330.  
  331. // Keep our minimum and maximum in mind.
  332. calculatedPrice = clamp(calculatedPrice, minPriceBeforeFees, maxPriceBeforeFees);
  333.  
  334.  
  335. // In case there's a buy order higher than the calculated price.
  336. if (histogram != null && histogram.highest_buy_order != null) {
  337. var buyOrderPrice = market.getPriceBeforeFees(histogram.highest_buy_order);
  338. if (buyOrderPrice > calculatedPrice)
  339. calculatedPrice = buyOrderPrice;
  340. }
  341.  
  342. return calculatedPrice;
  343. }
  344. //#endregion
  345.  
  346. //#region Integer helpers
  347. function getRandomInt(min, max) {
  348. return Math.floor(Math.random() * (max - min + 1)) + min;
  349. }
  350.  
  351. function getNumberOfDigits(x) {
  352. return (Math.log10((x ^ (x >> 31)) - (x >> 31)) | 0) + 1;
  353. }
  354.  
  355. function padLeftZero(str, max) {
  356. str = str.toString();
  357. return str.length < max ? padLeftZero("0" + str, max) : str;
  358. }
  359.  
  360. function replaceNonNumbers(str) {
  361. return str.replace(/\D/g, '');
  362. }
  363. //#endregion
  364.  
  365. //#region Steam Market
  366.  
  367. // Sell an item with a price in cents.
  368. // Price is before fees.
  369. SteamMarket.prototype.sellItem = function (item, price, callback /*err, data*/) {
  370. var sessionId = readCookie('sessionid');
  371. var itemId = item.assetid || item.id;
  372. $.ajax({
  373. type: "POST",
  374. url: 'https://steamcommunity.com/market/sellitem/',
  375. data: {
  376. sessionid: sessionId,
  377. appid: item.appid,
  378. contextid: item.contextid,
  379. assetid: itemId,
  380. amount: 1,
  381. price: price
  382. },
  383. success: function (data) {
  384. callback(ERROR_SUCCESS, data);
  385. },
  386. error: function (data) {
  387. return callback(ERROR_FAILED, data);
  388. },
  389. crossDomain: true,
  390. xhrFields: { withCredentials: true },
  391. dataType: 'json'
  392. });
  393. };
  394.  
  395. // Removes an item.
  396. // Item is the unique item id.
  397. SteamMarket.prototype.removeListing = function (item, callback /*err, data*/) {
  398. var sessionId = readCookie('sessionid');
  399. $.ajax({
  400. type: "POST",
  401. url: window.location.protocol + '//steamcommunity.com/market/removelisting/' + item,
  402. data: {
  403. sessionid: sessionId
  404. },
  405. success: function (data) {
  406. callback(ERROR_SUCCESS, data);
  407. },
  408. error: function () {
  409. return callback(ERROR_FAILED);
  410. },
  411. crossDomain: true,
  412. xhrFields: { withCredentials: true },
  413. dataType: 'json'
  414. });
  415. };
  416.  
  417. // Get the price history for an item.
  418. //
  419. // PriceHistory is an array of prices in the form [data, price, number sold].
  420. // Example: [["Fri, 19 Jul 2013 01:00:00 +0000",7.30050206184,362]]
  421. // Prices are ordered by oldest to most recent.
  422. // Price is inclusive of fees.
  423. SteamMarket.prototype.getPriceHistory = function (item, cache, callback) {
  424. try {
  425. var market_name = getMarketHashName(item);
  426. if (market_name == null) {
  427. callback(ERROR_FAILED);
  428. return;
  429. }
  430.  
  431. var appid = item.appid;
  432.  
  433. if (cache) {
  434. var storage_hash = 'pricehistory_' + appid + '+' + market_name;
  435.  
  436. storageSession.getItem(storage_hash)
  437. .then(function (value) {
  438. if (value != null)
  439. callback(ERROR_SUCCESS, value, true);
  440. else
  441. market.getCurrentPriceHistory(appid, market_name, callback);
  442. })
  443. .catch(function (error) {
  444. market.getCurrentPriceHistory(appid, market_name, callback);
  445. });
  446. } else
  447. market.getCurrentPriceHistory(appid, market_name, callback);
  448. } catch (e) {
  449. return callback(ERROR_FAILED);
  450. }
  451. };
  452.  
  453. SteamMarket.prototype.getGooValue = function (item, callback) {
  454. try {
  455. var sessionId = readCookie('sessionid');
  456. $.ajax({
  457. type: "GET",
  458. url: this.inventoryUrlBase + 'ajaxgetgoovalue/',
  459. data: {
  460. sessionid: sessionId,
  461. appid: item.market_fee_app,
  462. assetid: item.assetid,
  463. contextid: item.contextid
  464. },
  465. success: function (data) {
  466. callback(ERROR_SUCCESS, data);
  467. },
  468. error: function (data) {
  469. return callback(ERROR_FAILED, data);
  470. },
  471. crossDomain: true,
  472. xhrFields: { withCredentials: true },
  473. dataType: 'json'
  474. });
  475. } catch (e) {
  476. return callback(ERROR_FAILED);
  477. }
  478. //http://steamcommunity.com/auction/ajaxgetgoovalueforitemtype/?appid=582980&item_type=18&border_color=0
  479. // OR
  480. //http://steamcommunity.com/my/ajaxgetgoovalue/?sessionid=xyz&appid=535690&assetid=4830605461&contextid=6
  481. //sessionid=xyz
  482. //appid = 535690
  483. //assetid = 4830605461
  484. //contextid = 6
  485. }
  486.  
  487.  
  488. // Grinds the item into gems.
  489. SteamMarket.prototype.grindIntoGoo = function (item, callback) {
  490. try {
  491. var sessionId = readCookie('sessionid');
  492. $.ajax({
  493. type: "POST",
  494. url: this.inventoryUrlBase + 'ajaxgrindintogoo/',
  495. data: {
  496. sessionid: sessionId,
  497. appid: item.market_fee_app,
  498. assetid: item.assetid,
  499. contextid: item.contextid,
  500. goo_value_expected: item.goo_value_expected
  501. },
  502. success: function (data) {
  503. callback(ERROR_SUCCESS, data);
  504. },
  505. error: function (data) {
  506. return callback(ERROR_FAILED, data);
  507. },
  508. crossDomain: true,
  509. xhrFields: { withCredentials: true },
  510. dataType: 'json'
  511. });
  512. } catch (e) {
  513. return callback(ERROR_FAILED);
  514. }
  515.  
  516. //sessionid = xyz
  517. //appid = 535690
  518. //assetid = 4830605461
  519. //contextid = 6
  520. //goo_value_expected = 10
  521. //http://steamcommunity.com/my/ajaxgrindintogoo/
  522. }
  523.  
  524. // Get the current price history for an item.
  525. SteamMarket.prototype.getCurrentPriceHistory = function (appid, market_name, callback) {
  526. var url = window.location.protocol +
  527. '//steamcommunity.com/market/pricehistory/?appid=' +
  528. appid +
  529. '&market_hash_name=' +
  530. market_name;
  531.  
  532. $.get(url,
  533. function (data) {
  534. if (!data || !data.success || !data.prices) {
  535. callback(ERROR_DATA);
  536. return;
  537. }
  538.  
  539. // Multiply prices so they're in pennies.
  540. for (var i = 0; i < data.prices.length; i++) {
  541. data.prices[i][1] *= 100;
  542. data.prices[i][2] = parseInt(data.prices[i][2]);
  543. }
  544.  
  545. // Store the price history in the session storage.
  546. var storage_hash = 'pricehistory_' + appid + '+' + market_name;
  547. storageSession.setItem(storage_hash, data.prices);
  548.  
  549. callback(ERROR_SUCCESS, data.prices, false);
  550. },
  551. 'json')
  552. .fail(function (data) {
  553. if (!data || !data.responseJSON) {
  554. return callback(ERROR_FAILED);
  555. }
  556. if (!data.responseJSON.success) {
  557. callback(ERROR_DATA);
  558. return;
  559. }
  560. return callback(ERROR_FAILED);
  561. });
  562. }
  563.  
  564. // Get the item name id from a market item.
  565. //
  566. // This id never changes so we can store this in the persistent storage.
  567. SteamMarket.prototype.getMarketItemNameId = function (item, callback) {
  568. try {
  569. var market_name = getMarketHashName(item);
  570. if (market_name == null) {
  571. callback(ERROR_FAILED);
  572. return;
  573. }
  574.  
  575. var appid = item.appid;
  576. var storage_hash = 'itemnameid_' + appid + '+' + market_name;
  577.  
  578. storagePersistent.getItem(storage_hash)
  579. .then(function (value) {
  580. if (value != null)
  581. callback(ERROR_SUCCESS, value);
  582. else
  583. return market.getCurrentMarketItemNameId(appid, market_name, callback);
  584. })
  585. .catch(function (error) {
  586. return market.getCurrentMarketItemNameId(appid, market_name, callback);
  587. });
  588. } catch (e) {
  589. return callback(ERROR_FAILED);
  590. }
  591. }
  592.  
  593. // Get the item name id from a market item.
  594. SteamMarket.prototype.getCurrentMarketItemNameId = function (appid, market_name, callback) {
  595. var url = window.location.protocol + '//steamcommunity.com/market/listings/' + appid + '/' + market_name;
  596. $.get(url,
  597. function (page) {
  598. var matches = /Market_LoadOrderSpread\( (.+) \);/.exec(page);
  599. if (matches == null) {
  600. callback(ERROR_DATA);
  601. return;
  602. }
  603.  
  604. var item_nameid = matches[1];
  605.  
  606. // Store the item name id in the persistent storage.
  607. var storage_hash = 'itemnameid_' + appid + '+' + market_name;
  608. storagePersistent.setItem(storage_hash, item_nameid);
  609.  
  610. callback(ERROR_SUCCESS, item_nameid);
  611. })
  612. .fail(function (e) {
  613. return callback(ERROR_FAILED, e.status);
  614. });
  615. };
  616.  
  617. // Get the sales listings for this item in the market, with more information.
  618. //
  619. //{
  620. //"success" : 1,
  621. //"sell_order_table" : "<table class=\"market_commodity_orders_table\"><tr><th align=\"right\">Price<\/th><th align=\"right\">Quantity<\/th><\/tr><tr><td align=\"right\" class=\"\">0,04\u20ac<\/td><td align=\"right\">311<\/td><\/tr><tr><td align=\"right\" class=\"\">0,05\u20ac<\/td><td align=\"right\">895<\/td><\/tr><tr><td align=\"right\" class=\"\">0,06\u20ac<\/td><td align=\"right\">495<\/td><\/tr><tr><td align=\"right\" class=\"\">0,07\u20ac<\/td><td align=\"right\">174<\/td><\/tr><tr><td align=\"right\" class=\"\">0,08\u20ac<\/td><td align=\"right\">49<\/td><\/tr><tr><td align=\"right\" class=\"\">0,09\u20ac or more<\/td><td align=\"right\">41<\/td><\/tr><\/table>",
  622. //"sell_order_summary" : "<span class=\"market_commodity_orders_header_promote\">1965<\/span> for sale starting at <span class=\"market_commodity_orders_header_promote\">0,04\u20ac<\/span>",
  623. //"buy_order_table" : "<table class=\"market_commodity_orders_table\"><tr><th align=\"right\">Price<\/th><th align=\"right\">Quantity<\/th><\/tr><tr><td align=\"right\" class=\"\">0,03\u20ac<\/td><td align=\"right\">93<\/td><\/tr><\/table>",
  624. //"buy_order_summary" : "<span class=\"market_commodity_orders_header_promote\">93<\/span> requests to buy at <span class=\"market_commodity_orders_header_promote\">0,03\u20ac<\/span> or lower",
  625. //"highest_buy_order" : "3",
  626. //"lowest_sell_order" : "4",
  627. //"buy_order_graph" : [[0.03, 93, "93 buy orders at 0,03\u20ac or higher"]],
  628. //"sell_order_graph" : [[0.04, 311, "311 sell orders at 0,04\u20ac or lower"], [0.05, 1206, "1,206 sell orders at 0,05\u20ac or lower"], [0.06, 1701, "1,701 sell orders at 0,06\u20ac or lower"], [0.07, 1875, "1,875 sell orders at 0,07\u20ac or lower"], [0.08, 1924, "1,924 sell orders at 0,08\u20ac or lower"], [0.09, 1934, "1,934 sell orders at 0,09\u20ac or lower"], [0.1, 1936, "1,936 sell orders at 0,10\u20ac or lower"], [0.11, 1937, "1,937 sell orders at 0,11\u20ac or lower"], [0.12, 1944, "1,944 sell orders at 0,12\u20ac or lower"], [0.14, 1945, "1,945 sell orders at 0,14\u20ac or lower"]],
  629. //"graph_max_y" : 3000,
  630. //"graph_min_x" : 0.03,
  631. //"graph_max_x" : 0.14,
  632. //"price_prefix" : "",
  633. //"price_suffix" : "\u20ac"
  634. //}
  635. SteamMarket.prototype.getItemOrdersHistogram = function (item, cache, callback) {
  636. try {
  637. var market_name = getMarketHashName(item);
  638. if (market_name == null) {
  639. callback(ERROR_FAILED);
  640. return;
  641. }
  642.  
  643. var appid = item.appid;
  644.  
  645. if (cache) {
  646. var storage_hash = 'itemordershistogram_' + appid + '+' + market_name;
  647. storageSession.getItem(storage_hash)
  648. .then(function (value) {
  649. if (value != null)
  650. callback(ERROR_SUCCESS, value, true);
  651. else {
  652. market.getCurrentItemOrdersHistogram(item, market_name, callback);
  653. }
  654. })
  655. .catch(function (error) {
  656. market.getCurrentItemOrdersHistogram(item, market_name, callback);
  657. });
  658. } else {
  659. market.getCurrentItemOrdersHistogram(item, market_name, callback);
  660. }
  661.  
  662. } catch (e) {
  663. return callback(ERROR_FAILED);
  664. }
  665. };
  666.  
  667. // Get the sales listings for this item in the market, with more information.
  668. SteamMarket.prototype.getCurrentItemOrdersHistogram = function (item, market_name, callback) {
  669. market.getMarketItemNameId(item,
  670. function (error, item_nameid) {
  671. if (error) {
  672. if (item_nameid != 429) // 429 = Too many requests made.
  673. callback(ERROR_DATA);
  674. else
  675. callback(ERROR_FAILED);
  676. return;
  677. }
  678. var url = window.location.protocol +
  679. '//steamcommunity.com/market/itemordershistogram?language=english&currency=' +
  680. currencyId +
  681. '&item_nameid=' +
  682. item_nameid +
  683. '&two_factor=0';
  684.  
  685. $.get(url,
  686. function (histogram) {
  687. // Store the histogram in the session storage.
  688. var storage_hash = 'itemordershistogram_' + item.appid + '+' + market_name;
  689. storageSession.setItem(storage_hash, histogram);
  690.  
  691. callback(ERROR_SUCCESS, histogram, false);
  692. })
  693. .fail(function () {
  694. return callback(ERROR_FAILED, null);
  695. });
  696. });
  697. };
  698.  
  699. // Calculate the price before fees (seller price) from the buyer price
  700. SteamMarket.prototype.getPriceBeforeFees = function (price, item) {
  701. var publisherFee = -1;
  702.  
  703. if (item != null) {
  704. if (item.market_fee != null)
  705. publisherFee = item.market_fee;
  706. else if (item.description != null && item.description.market_fee != null)
  707. publisherFee = item.description.market_fee;
  708. }
  709.  
  710. if (publisherFee == -1) {
  711. if (this.walletInfo != null)
  712. publisherFee = this.walletInfo['wallet_publisher_fee_percent_default'];
  713. else
  714. publisherFee = 0.10;
  715. }
  716.  
  717. price = Math.round(price);
  718. var feeInfo = CalculateFeeAmount(price, publisherFee, this.walletInfo);
  719. return price - feeInfo.fees;
  720. };
  721.  
  722. // Calculate the buyer price from the seller price
  723. SteamMarket.prototype.getPriceIncludingFees = function (price, item) {
  724. var publisherFee = -1;
  725. if (item != null) {
  726. if (item.market_fee != null)
  727. publisherFee = item.market_fee;
  728. else if (item.description != null && item.description.market_fee != null)
  729. publisherFee = item.description.market_fee;
  730. }
  731. if (publisherFee == -1) {
  732. if (this.walletInfo != null)
  733. publisherFee = this.walletInfo['wallet_publisher_fee_percent_default'];
  734. else
  735. publisherFee = 0.10;
  736. }
  737.  
  738. price = Math.round(price);
  739. var feeInfo = CalculateAmountToSendForDesiredReceivedAmount(price, publisherFee, this.walletInfo);
  740. return feeInfo.amount;
  741. };
  742. //#endregion
  743.  
  744. function replaceAll(str, find, replace) {
  745. return str.replace(new RegExp(find, 'g'), replace);
  746. }
  747.  
  748. // Cannot use encodeURI / encodeURIComponent, Steam only escapes certain characters.
  749. function escapeURI(name) {
  750. var previousName = '';
  751. while (previousName != name) {
  752. previousName = name;
  753. name = name.replace('?', '%3F')
  754. .replace('#', '%23')
  755. .replace(' ', '%09');
  756. }
  757. return name;
  758. }
  759.  
  760. //#region Steam Market / Inventory helpers
  761. function getMarketHashName(item) {
  762. if (item == null)
  763. return null;
  764.  
  765. if (item.description != null && item.description.market_hash_name != null)
  766. return escapeURI(item.description.market_hash_name);
  767.  
  768. if (item.description != null && item.description.name != null)
  769. return escapeURI(item.description.name);
  770.  
  771. if (item.market_hash_name != null)
  772. return escapeURI(item.market_hash_name);
  773.  
  774. if (item.name != null)
  775. return escapeURI(item.name);
  776.  
  777. return null;
  778. }
  779.  
  780. function getIsTradingCard(item) {
  781. if (item == null)
  782. return false;
  783.  
  784. // This is available on the inventory page.
  785. var tags = item.tags != null
  786. ? item.tags
  787. : (item.description != null && item.description.tags != null
  788. ? item.description.tags
  789. : null);
  790. if (tags != null) {
  791. var isTaggedAsTradingCard = false;
  792. tags.forEach(function (arrayItem) {
  793. if (arrayItem.category == 'item_class')
  794. if (arrayItem.internal_name == 'item_class_2') // trading card.
  795. isTaggedAsTradingCard = true;
  796. });
  797. if (isTaggedAsTradingCard)
  798. return true;
  799. }
  800.  
  801. // This is available on the market page.
  802. if (item.owner_actions != null) {
  803. for (var i = 0; i < item.owner_actions.length; i++) {
  804. if (item.owner_actions[i].link == null)
  805. continue;
  806.  
  807. // Cards include a link to the gamecard page.
  808. // For example: "http://steamcommunity.com/my/gamecards/503820/".
  809. if (item.owner_actions[i].link.toString().toLowerCase().includes('gamecards'))
  810. return true;
  811. }
  812. }
  813.  
  814. // A fallback for the market page (only works with language on English).
  815. if (item.type != null && item.type.toLowerCase().includes('trading card'))
  816. return true;
  817.  
  818. return false;
  819. }
  820.  
  821. function getIsFoilTradingCard(item) {
  822. if (!getIsTradingCard(item))
  823. return false;
  824.  
  825. // This is available on the inventory page.
  826. var tags = item.tags != null
  827. ? item.tags
  828. : (item.description != null && item.description.tags != null
  829. ? item.description.tags
  830. : null);
  831. if (tags != null) {
  832. var isTaggedAsFoilTradingCard = false;
  833. tags.forEach(function (arrayItem) {
  834. if (arrayItem.category == 'cardborder')
  835. if (arrayItem.internal_name == 'cardborder_1') // foil border.
  836. isTaggedAsFoilTradingCard = true;
  837. });
  838. if (isTaggedAsFoilTradingCard)
  839. return true;
  840. }
  841.  
  842. // This is available on the market page.
  843. if (item.owner_actions != null) {
  844. for (var i = 0; i < item.owner_actions.length; i++) {
  845. if (item.owner_actions[i].link == null)
  846. continue;
  847.  
  848. // Cards include a link to the gamecard page.
  849. // The border parameter specifies the foil cards.
  850. // For example: "http://steamcommunity.com/my/gamecards/503820/?border=1".
  851. if (item.owner_actions[i].link.toString().toLowerCase().includes('gamecards') &&
  852. item.owner_actions[i].link.toString().toLowerCase().includes('border'))
  853. return true;
  854. }
  855. }
  856.  
  857. // A fallback for the market page (only works with language on English).
  858. if (item.type != null && item.type.toLowerCase().includes('foil trading card'))
  859. return true;
  860.  
  861. return false;
  862. }
  863.  
  864. function CalculateFeeAmount(amount, publisherFee, walletInfo) {
  865. if (walletInfo == null || !walletInfo['wallet_fee']) {
  866. return { fees: 0 };
  867. }
  868.  
  869. publisherFee = (publisherFee == null) ? 0 : publisherFee;
  870. // Since CalculateFeeAmount has a Math.floor, we could be off a cent or two. Let's check:
  871. var iterations = 0; // shouldn't be needed, but included to be sure nothing unforseen causes us to get stuck
  872. var nEstimatedAmountOfWalletFundsReceivedByOtherParty =
  873. parseInt((amount - parseInt(walletInfo['wallet_fee_base'])) /
  874. (parseFloat(walletInfo['wallet_fee_percent']) + parseFloat(publisherFee) + 1));
  875. var bEverUndershot = false;
  876. var fees = CalculateAmountToSendForDesiredReceivedAmount(nEstimatedAmountOfWalletFundsReceivedByOtherParty,
  877. publisherFee,
  878. walletInfo);
  879. while (fees.amount != amount && iterations < 10) {
  880. if (fees.amount > amount) {
  881. if (bEverUndershot) {
  882. fees = CalculateAmountToSendForDesiredReceivedAmount(
  883. nEstimatedAmountOfWalletFundsReceivedByOtherParty - 1,
  884. publisherFee,
  885. walletInfo);
  886. fees.steam_fee += (amount - fees.amount);
  887. fees.fees += (amount - fees.amount);
  888. fees.amount = amount;
  889. break;
  890. } else {
  891. nEstimatedAmountOfWalletFundsReceivedByOtherParty--;
  892. }
  893. } else {
  894. bEverUndershot = true;
  895. nEstimatedAmountOfWalletFundsReceivedByOtherParty++;
  896. }
  897. fees = CalculateAmountToSendForDesiredReceivedAmount(nEstimatedAmountOfWalletFundsReceivedByOtherParty,
  898. publisherFee,
  899. walletInfo);
  900. iterations++;
  901. }
  902. // fees.amount should equal the passed in amount
  903. return fees;
  904. }
  905.  
  906. // Clamps cur between min and max (inclusive).
  907. function clamp(cur, min, max) {
  908. if (cur < min)
  909. cur = min;
  910.  
  911. if (cur > max)
  912. cur = max;
  913.  
  914. return cur;
  915. }
  916.  
  917. // Strangely named function, it actually works out the fees and buyer price for a seller price
  918. function CalculateAmountToSendForDesiredReceivedAmount(receivedAmount, publisherFee, walletInfo) {
  919. if (walletInfo == null || !walletInfo['wallet_fee']) {
  920. return { amount: receivedAmount };
  921. }
  922.  
  923. publisherFee = (publisherFee == null) ? 0 : publisherFee;
  924. var nSteamFee = parseInt(Math.floor(Math.max(receivedAmount * parseFloat(walletInfo['wallet_fee_percent']),
  925. walletInfo['wallet_fee_minimum']) +
  926. parseInt(walletInfo['wallet_fee_base'])));
  927. var nPublisherFee = parseInt(Math.floor(publisherFee > 0 ? Math.max(receivedAmount * publisherFee, 1) : 0));
  928. var nAmountToSend = receivedAmount + nSteamFee + nPublisherFee;
  929. return {
  930. steam_fee: nSteamFee,
  931. publisher_fee: nPublisherFee,
  932. fees: nSteamFee + nPublisherFee,
  933. amount: parseInt(nAmountToSend)
  934. };
  935. }
  936.  
  937. function readCookie(name) {
  938. var nameEQ = name + "=";
  939. var ca = document.cookie.split(';');
  940. for (var i = 0; i < ca.length; i++) {
  941. var c = ca[i];
  942. while (c.charAt(0) == ' ')
  943. c = c.substring(1, c.length);
  944. if (c.indexOf(nameEQ) == 0)
  945. return decodeURIComponent(c.substring(nameEQ.length, c.length));
  946. }
  947. return null;
  948. }
  949. //#endregion
  950.  
  951. //#region Logging
  952. var userScrolled = false;
  953. var logger = document.createElement('div');
  954. logger.setAttribute('id', 'logger');
  955.  
  956. function updateScroll() {
  957. if (!userScrolled) {
  958. var element = document.getElementById("logger");
  959. element.scrollTop = element.scrollHeight;
  960. }
  961. }
  962.  
  963. function logDOM(text) {
  964. logger.innerHTML += text + '<br/>';
  965.  
  966. updateScroll();
  967. }
  968.  
  969. function clearLogDOM() {
  970. logger.innerHTML = '';
  971.  
  972. updateScroll();
  973. }
  974.  
  975. function logConsole(text) {
  976. if (enableConsoleLog) {
  977. console.log(text);
  978. }
  979. }
  980. //#endregion
  981.  
  982. //#region Inventory
  983. if (currentPage == PAGE_INVENTORY) {
  984. var sellQueue = async.queue(function (task, next) {
  985. market.sellItem(task.item,
  986. task.sellPrice,
  987. function (err, data) {
  988. totalNumberOfProcessedQueueItems++;
  989.  
  990. var digits = getNumberOfDigits(totalNumberOfQueuedItems);
  991. var itemId = task.item.assetid || task.item.id;
  992. var itemName = task.item.name || task.item.description.name;
  993. var padLeft = padLeftZero('' + totalNumberOfProcessedQueueItems, digits) + ' / ' + totalNumberOfQueuedItems;
  994.  
  995. if (!err) {
  996. logDOM(padLeft +
  997. ' - ' +
  998. itemName +
  999. ' added to market for ' +
  1000. (market.getPriceIncludingFees(task.sellPrice) / 100.0).toFixed(2) +
  1001. currencySymbol +
  1002. '.');
  1003.  
  1004. $('#' + task.item.appid + '_' + task.item.contextid + '_' + itemId)
  1005. .css('background', COLOR_SUCCESS);
  1006. } else {
  1007. if (data.responseJSON.message != null)
  1008. logDOM(padLeft +
  1009. ' - ' +
  1010. itemName +
  1011. ' not added to market because ' +
  1012. data.responseJSON.message[0].toLowerCase() +
  1013. data.responseJSON.message.slice(1));
  1014. else
  1015. logDOM(padLeft + ' - ' + itemName + ' not added to market.');
  1016.  
  1017. $('#' + task.item.appid + '_' + task.item.contextid + '_' + itemId)
  1018. .css('background', COLOR_ERROR);
  1019. }
  1020.  
  1021. next();
  1022. });
  1023. },
  1024. 1);
  1025.  
  1026. sellQueue.drain = function () {
  1027. if (itemQueue.length() == 0 && sellQueue.length() == 0 && scrapQueue.length() == 0) {
  1028. $('#inventory_items_spinner').remove();
  1029. }
  1030. }
  1031.  
  1032. function sellAllItems(appId) {
  1033. loadAllInventories().then(function () {
  1034. var items = getInventoryItems();
  1035. var filteredItems = [];
  1036.  
  1037. items.forEach(function (item) {
  1038. if (!item.marketable) {
  1039. return;
  1040. }
  1041.  
  1042. filteredItems.push(item);
  1043. });
  1044.  
  1045. sellItems(filteredItems);
  1046. },
  1047. function () {
  1048. logDOM('Could not retrieve the inventory...');
  1049. });
  1050. }
  1051.  
  1052. function sellAllCards() {
  1053. loadAllInventories().then(function () {
  1054. var items = getInventoryItems();
  1055. var filteredItems = [];
  1056.  
  1057. items.forEach(function (item) {
  1058. if (!getIsTradingCard(item) || !item.marketable) {
  1059. return;
  1060. }
  1061.  
  1062. filteredItems.push(item);
  1063. });
  1064.  
  1065. sellItems(filteredItems);
  1066. },
  1067. function () {
  1068. logDOM('Could not retrieve the inventory...');
  1069. });
  1070. }
  1071.  
  1072. var scrapQueue = async.queue(function (item, next) {
  1073. scrapQueueWorker(item, function (success) {
  1074. if (success) {
  1075. setTimeout(function () { next(); }, 250);
  1076. } else {
  1077. var delay = numberOfFailedRequests > 1
  1078. ? getRandomInt(30000, 45000)
  1079. : getRandomInt(1000, 1500);
  1080.  
  1081. if (numberOfFailedRequests > 3)
  1082. numberOfFailedRequests = 0;
  1083.  
  1084. setTimeout(function () {
  1085. next();
  1086. }, delay);
  1087. }
  1088. });
  1089. }, 1);
  1090.  
  1091. scrapQueue.drain = function () {
  1092. if (itemQueue.length() == 0 && sellQueue.length() == 0 && scrapQueue.length() == 0) {
  1093. $('#inventory_items_spinner').remove();
  1094. }
  1095. }
  1096.  
  1097. function scrapQueueWorker(item, callback) {
  1098. var failed = 0;
  1099. var itemName = item.name || item.description.name;
  1100. var itemId = item.assetid || item.id;
  1101.  
  1102. market.getGooValue(item,
  1103. function (err, goo) {
  1104. totalNumberOfProcessedQueueItems++;
  1105.  
  1106. var digits = getNumberOfDigits(totalNumberOfQueuedItems);
  1107. var padLeft = padLeftZero('' + totalNumberOfProcessedQueueItems, digits) + ' / ' + totalNumberOfQueuedItems;
  1108.  
  1109. if (err != ERROR_SUCCESS) {
  1110. logConsole('Failed to get gems value for ' + itemName);
  1111. logDOM(padLeft + ' - ' + itemName + ' not turned into gems due to missing gems value.');
  1112.  
  1113. $('#' + item.appid + '_' + item.contextid + '_' + itemId).css('background', COLOR_ERROR);
  1114. return callback(false);
  1115. }
  1116.  
  1117. item.goo_value_expected = parseInt(goo.goo_value);
  1118.  
  1119. market.grindIntoGoo(item,
  1120. function (err, result) {
  1121. if (err != ERROR_SUCCESS) {
  1122. logConsole('Failed to turn item into gems for ' + itemName);
  1123. logDOM(padLeft + ' - ' + itemName + ' not turned into gems due to unknown error.');
  1124.  
  1125. $('#' + item.appid + '_' + item.contextid + '_' + itemId).css('background', COLOR_ERROR);
  1126. return callback(false);
  1127. }
  1128.  
  1129. logConsole('============================')
  1130. logConsole(itemName);
  1131. logConsole('Turned into ' + goo.goo_value + ' gems');
  1132. logDOM(padLeft + ' - ' + itemName + ' turned into ' + item.goo_value_expected + ' gems.');
  1133. $('#' + item.appid + '_' + item.contextid + '_' + itemId).css('background', COLOR_SUCCESS);
  1134.  
  1135. callback(true);
  1136. });
  1137. });
  1138. }
  1139.  
  1140. // Turns the selected items into gems.
  1141. function turnSelectedItemsIntoGems() {
  1142. var ids = [];
  1143. $('.inventory_ctn').each(function () {
  1144. $(this).find('.inventory_page').each(function () {
  1145. var inventory_page = this;
  1146.  
  1147. $(inventory_page).find('.itemHolder').each(function () {
  1148. if (!$(this).hasClass('ui-selected'))
  1149. return;
  1150.  
  1151. $(this).find('.item').each(function () {
  1152. var matches = this.id.match(/_(\-?\d+)$/);
  1153. if (matches) {
  1154. ids.push(matches[1]);
  1155. }
  1156. });
  1157. });
  1158. });
  1159. });
  1160.  
  1161. loadAllInventories().then(function () {
  1162. var items = getInventoryItems();
  1163.  
  1164. var numberOfQueuedItems = 0;
  1165. items.forEach(function (item) {
  1166. // Ignored queued items.
  1167. if (item.queued != null) {
  1168. return;
  1169. }
  1170.  
  1171. if (item.owner_actions == null) {
  1172. return;
  1173. }
  1174.  
  1175. var canTurnIntoGems = false;
  1176. for (var owner_action in item.owner_actions) {
  1177. if (item.owner_actions[owner_action].link != null && item.owner_actions[owner_action].link.includes('GetGooValue')) {
  1178. canTurnIntoGems = true;
  1179. }
  1180. }
  1181.  
  1182. if (!canTurnIntoGems)
  1183. return;
  1184.  
  1185. var itemId = item.assetid || item.id;
  1186. if (ids.indexOf(itemId) !== -1) {
  1187. item.queued = true;
  1188. scrapQueue.push(item);
  1189. numberOfQueuedItems++;
  1190. }
  1191. });
  1192.  
  1193. if (numberOfQueuedItems > 0) {
  1194. totalNumberOfQueuedItems += numberOfQueuedItems;
  1195.  
  1196. $('#inventory_items_spinner').remove();
  1197. $('#inventory_sell_buttons').append('<div id="inventory_items_spinner">' +
  1198. spinnerBlock +
  1199. '<div style="text-align:center">Processing ' + numberOfQueuedItems + ' items</div>' +
  1200. '</div>');
  1201. }
  1202. }, function () {
  1203. logDOM('Could not retrieve the inventory...');
  1204. });
  1205. }
  1206.  
  1207. function sellSelectedItems() {
  1208. var ids = [];
  1209. $('.inventory_ctn').each(function () {
  1210. $(this).find('.inventory_page').each(function () {
  1211. var inventory_page = this;
  1212.  
  1213. $(inventory_page).find('.itemHolder').each(function () {
  1214. if (!$(this).hasClass('ui-selected'))
  1215. return;
  1216.  
  1217. $(this).find('.item').each(function () {
  1218. var matches = this.id.match(/_(\-?\d+)$/);
  1219. if (matches) {
  1220. ids.push(matches[1]);
  1221. }
  1222. });
  1223. });
  1224. });
  1225. });
  1226.  
  1227. loadAllInventories().then(function () {
  1228. var items = getInventoryItems();
  1229. var filteredItems = [];
  1230.  
  1231. items.forEach(function (item) {
  1232. if (!item.marketable) {
  1233. return;
  1234. }
  1235.  
  1236. var itemId = item.assetid || item.id;
  1237. if (ids.indexOf(itemId) !== -1) {
  1238. filteredItems.push(item);
  1239. }
  1240. });
  1241.  
  1242. sellItems(filteredItems);
  1243. }, function () {
  1244. logDOM('Could not retrieve the inventory...');
  1245. });
  1246. }
  1247.  
  1248. function sellItems(items) {
  1249. if (items.length == 0) {
  1250. logDOM('These items cannot be added to the market...');
  1251.  
  1252. return;
  1253. }
  1254.  
  1255. var numberOfQueuedItems = 0;
  1256.  
  1257. items.forEach(function (item, index, array) {
  1258. // Ignored queued items.
  1259. if (item.queued != null) {
  1260. return;
  1261. }
  1262.  
  1263. item.queued = true;
  1264. var itemId = item.assetid || item.id;
  1265. item.ignoreErrors = false;
  1266. itemQueue.push(item);
  1267. numberOfQueuedItems++;
  1268. });
  1269.  
  1270. if (numberOfQueuedItems > 0) {
  1271. totalNumberOfQueuedItems += numberOfQueuedItems;
  1272.  
  1273. $('#inventory_items_spinner').remove();
  1274. $('#inventory_sell_buttons').append('<div id="inventory_items_spinner">' +
  1275. spinnerBlock +
  1276. '<div style="text-align:center">Processing ' + numberOfQueuedItems + ' items</div>' +
  1277. '</div>');
  1278. }
  1279. }
  1280.  
  1281. var itemQueue = async.queue(function (item, next) {
  1282. itemQueueWorker(item,
  1283. item.ignoreErrors,
  1284. function (success, cached) {
  1285. if (success) {
  1286. setTimeout(function () {
  1287. next();
  1288. },
  1289. cached ? 0 : getRandomInt(1000, 1500));
  1290. } else {
  1291. if (!item.ignoreErrors) {
  1292. item.ignoreErrors = true;
  1293. itemQueue.push(item);
  1294. }
  1295.  
  1296. var delay = numberOfFailedRequests > 1
  1297. ? getRandomInt(30000, 45000)
  1298. : getRandomInt(1000, 1500);
  1299.  
  1300. if (numberOfFailedRequests > 3)
  1301. numberOfFailedRequests = 0;
  1302.  
  1303. setTimeout(function () {
  1304. next();
  1305. },
  1306. cached ? 0 : delay);
  1307. }
  1308. });
  1309. }, 1);
  1310.  
  1311. function itemQueueWorker(item, ignoreErrors, callback) {
  1312. var priceInfo = getPriceInformationFromItem(item);
  1313.  
  1314. var failed = 0;
  1315. var itemName = item.name || item.description.name;
  1316.  
  1317. market.getPriceHistory(item,
  1318. true,
  1319. function (err, history, cachedHistory) {
  1320. if (err) {
  1321. logConsole('Failed to get price history for ' + itemName);
  1322.  
  1323. if (err == ERROR_FAILED)
  1324. failed += 1;
  1325. }
  1326.  
  1327. market.getItemOrdersHistogram(item,
  1328. true,
  1329. function (err, histogram, cachedListings) {
  1330. if (err) {
  1331. logConsole('Failed to get orders histogram for ' + itemName);
  1332.  
  1333. if (err == ERROR_FAILED)
  1334. failed += 1;
  1335. }
  1336.  
  1337. if (failed > 0 && !ignoreErrors) {
  1338. return callback(false, cachedHistory && cachedListings);
  1339. }
  1340.  
  1341. logConsole('============================')
  1342. logConsole(itemName);
  1343.  
  1344. var sellPrice = calculateSellPriceBeforeFees(history,
  1345. histogram,
  1346. true,
  1347. priceInfo.minPriceBeforeFees,
  1348. priceInfo.maxPriceBeforeFees);
  1349.  
  1350. logConsole('Sell price: ' +
  1351. sellPrice / 100.0 +
  1352. ' (' +
  1353. market.getPriceIncludingFees(sellPrice) / 100.0 +
  1354. ')');
  1355.  
  1356. sellQueue.push({
  1357. item: item,
  1358. sellPrice: sellPrice
  1359. });
  1360.  
  1361. return callback(true, cachedHistory && cachedListings);
  1362. });
  1363. });
  1364. }
  1365.  
  1366. // Initialize the inventory UI.
  1367. function initializeInventoryUI() {
  1368. var isOwnInventory = g_ActiveUser.strSteamId == g_steamID;
  1369. var previousSelection = -1; // To store the index of the previous selection.
  1370. updateInventoryUI(isOwnInventory);
  1371.  
  1372. $('.games_list_tabs').on('click',
  1373. '*',
  1374. function () {
  1375. updateInventoryUI(isOwnInventory);
  1376. });
  1377.  
  1378. // Ignore selection on other user's inventories.
  1379. if (!isOwnInventory)
  1380. return;
  1381.  
  1382. var filter =
  1383. ".itemHolder:not([style*=none])"; // Steam adds 'display:none' to items while searching. These should not be selected while using shift/ctrl.
  1384. $('#inventories').selectable({
  1385. filter: filter,
  1386. selecting: function (e, ui) {
  1387. var selectedIndex =
  1388. $(ui.selecting.tagName, e.target).index(ui.selecting); // Get selected item index.
  1389. if (e.shiftKey && previousSelection > -1
  1390. ) { // If shift key was pressed and there is previous - select them all.
  1391. $(ui.selecting.tagName, e.target)
  1392. .slice(Math.min(previousSelection, selectedIndex),
  1393. 1 + Math.max(previousSelection, selectedIndex)).each(function () {
  1394. if ($(this).is(filter)) {
  1395. $(this).addClass('ui-selected');
  1396. }
  1397. });
  1398. previousSelection = -1; // Reset previous.
  1399. } else {
  1400. previousSelection = selectedIndex; // Save previous.
  1401. }
  1402. },
  1403. selected: function (e, ui) {
  1404. updateInventorySelection(ui.selected);
  1405. }
  1406. });
  1407. }
  1408.  
  1409. function updateInventorySelection(item) {
  1410. // Wait until g_ActiveInventory.selectedItem is identical to the selected UI item.
  1411. // This also makes sure that the new - and correct - item_info (iteminfo0 or iteminfo1) is visible.
  1412. var selectedItemIdUI = $('div', item).attr('id');
  1413. var selectedItemIdInventory = getActiveInventory().selectedItem.appid +
  1414. '_' +
  1415. getActiveInventory().selectedItem.contextid +
  1416. '_' +
  1417. getActiveInventory().selectedItem.assetid;
  1418. if (selectedItemIdUI !== selectedItemIdInventory) {
  1419. setTimeout(function () {
  1420. updateInventorySelection(item);
  1421. },
  1422. 250);
  1423.  
  1424. return;
  1425. }
  1426.  
  1427. var item_info = $('.inventory_iteminfo:visible').first();
  1428. if (item_info.html().indexOf('checkout/sendgift/') > -1) // Gifts have no market information.
  1429. return;
  1430.  
  1431. // Use a 'hard' item id instead of relying on the selected item_info (sometimes Steam temporarily changes the correct item (?)).
  1432. var item_info_id = item_info.attr('id');
  1433.  
  1434. // Move scrap to bottom, this is of little interest.
  1435. var scrap = $('#' + item_info_id + '_scrap_content');
  1436. scrap.next().insertBefore(scrap);
  1437.  
  1438. // Starting at prices are already retrieved in the table.
  1439. //$('#' + item_info_id + '_item_market_actions > div:nth-child(1) > div:nth-child(2)')
  1440. // .remove(); // Starting at: x,xx.
  1441.  
  1442. var market_hash_name = getMarketHashName(getActiveInventory().selectedItem);
  1443. if (market_hash_name == null)
  1444. return;
  1445.  
  1446. var appid = getActiveInventory().selectedItem.appid;
  1447. var item = { appid: parseInt(appid), description: { market_hash_name: market_hash_name } };
  1448.  
  1449. market.getItemOrdersHistogram(item,
  1450. false,
  1451. function (err, histogram) {
  1452. if (err) {
  1453. logConsole('Failed to get orders histogram for ' + (getActiveInventory().selectedItem.name || getActiveInventory().selectedItem.description.name));
  1454. return;
  1455. }
  1456.  
  1457. var groupMain = $('<div id="listings_group">' +
  1458. '<div><div id="listings_sell">Sell</div>' +
  1459. histogram.sell_order_table +
  1460. '</div>' +
  1461. '<div><div id="listings_buy">Buy</div>' +
  1462. histogram.buy_order_table +
  1463. '</div>' +
  1464. '</div>');
  1465.  
  1466. $('#' + item_info_id + '_item_market_actions > div').after(groupMain);
  1467.  
  1468. // Generate quick sell buttons.
  1469. var itemId = getActiveInventory().selectedItem.assetid || getActiveInventory().selectedItem.id;
  1470.  
  1471. // Ignored queued items.
  1472. if (getActiveInventory().selectedItem.queued != null) {
  1473. return;
  1474. }
  1475.  
  1476. var prices = [];
  1477.  
  1478. if (histogram != null && histogram.highest_buy_order != null) {
  1479. prices.push(parseInt(histogram.highest_buy_order));
  1480. }
  1481.  
  1482. if (histogram != null && histogram.lowest_sell_order != null) {
  1483. prices.push(parseInt(histogram.lowest_sell_order) - 1);
  1484. prices.push(parseInt(histogram.lowest_sell_order));
  1485. prices.push(parseInt(histogram.lowest_sell_order) + 1);
  1486. }
  1487.  
  1488. var priceInformation = getPriceInformationFromItem(getActiveInventory().selectedItem);
  1489. prices.push(priceInformation.minPrice);
  1490. prices.push(priceInformation.maxPrice);
  1491.  
  1492. prices = prices.filter((v, i) => prices.indexOf(v) === i).sort((a, b) => a - b);
  1493.  
  1494. var buttons = '<br/>';
  1495. prices.forEach(function (e) {
  1496. buttons +=
  1497. '<a class="item_market_action_button item_market_action_button_green quick_sell" id="quick_sell' +
  1498. e +
  1499. '">' +
  1500. '<span class="item_market_action_button_edge item_market_action_button_left"></span>' +
  1501. '<span class="item_market_action_button_contents">' +
  1502. (e / 100.0) +
  1503. currencySymbol +
  1504. '</span>' +
  1505. '<span class="item_market_action_button_edge item_market_action_button_right"></span>' +
  1506. '<span class="item_market_action_button_preload"></span>' +
  1507. '</a>'
  1508. });
  1509.  
  1510. $('#' + item_info_id + '_item_market_actions', item_info).append(buttons);
  1511.  
  1512. $('.quick_sell').on('click',
  1513. function () {
  1514. var price = $(this).attr('id').replace('quick_sell', '');
  1515. price = market.getPriceBeforeFees(price);
  1516.  
  1517. totalNumberOfQueuedItems++;
  1518.  
  1519. sellQueue.push({
  1520. item: getActiveInventory().selectedItem,
  1521. sellPrice: price
  1522. });
  1523. });
  1524. });
  1525. }
  1526.  
  1527. // Update the inventory UI.
  1528. function updateInventoryUI(isOwnInventory) {
  1529. // Remove previous containers (e.g., when a user changes inventory).
  1530. $('#inventory_sell_buttons').remove();
  1531. $('#price_options').remove();
  1532. $('#inventory_reload_button').remove();
  1533.  
  1534. $('#see_settings').remove();
  1535. $('#global_action_menu')
  1536. .prepend('<span id="see_settings"><a href="javascript:void(0)">⬖ Steam Economy Enhancer</a></span>');
  1537. $('#see_settings').on('click', '*', () => openSettings());
  1538.  
  1539. var appId = getActiveInventory().m_appid;
  1540. var showMiscOptions = appId == 753;
  1541.  
  1542. var sellButtons = $('<div id="inventory_sell_buttons" style="margin-bottom:12px;">' +
  1543. '<a class="btn_green_white_innerfade btn_medium_wide sell_all"><span>Sell All Items</span></a>&nbsp;&nbsp;&nbsp;' +
  1544. '<a class="btn_green_white_innerfade btn_medium_wide sell_selected"><span>Sell Selected Items</span></a>&nbsp;&nbsp;&nbsp;' +
  1545. (showMiscOptions
  1546. ? '<a class="btn_green_white_innerfade btn_medium_wide turn_into_gems"><span>Turn Selected Items Into Gems</span></a>&nbsp;&nbsp;&nbsp;' +
  1547. '<a class="btn_darkblue_white_innerfade btn_medium_wide sell_all_cards"><span>Sell All Cards</span></a>&nbsp;&nbsp;&nbsp;'
  1548. : '') +
  1549. '</div>');
  1550.  
  1551. var reloadButton =
  1552. $(
  1553. '<a id="inventory_reload_button" class="btn_darkblue_white_innerfade btn_medium_wide reload_inventory" style="margin-right:12px"><span>Reload Inventory</span></a>');
  1554.  
  1555. $('#inventory_logos')[0].style.height = 'auto';
  1556.  
  1557. $('#inventory_applogo').hide(); // Hide the Steam/game logo, we don't need to see it twice.
  1558. $('#inventory_applogo').after(logger);
  1559.  
  1560. $("#logger").on('scroll',
  1561. function () {
  1562. var hasUserScrolledToBottom =
  1563. $("#logger").prop('scrollHeight') - $("#logger").prop('clientHeight') <=
  1564. $("#logger").prop('scrollTop') + 1;
  1565. userScrolled = !hasUserScrolledToBottom;
  1566. });
  1567.  
  1568. // Only add buttons on the user's inventory.
  1569. if (isOwnInventory) {
  1570. $('#inventory_applogo').after(sellButtons);
  1571.  
  1572. // Add bindings to sell buttons.
  1573. $('.sell_all').on('click',
  1574. '*',
  1575. function () {
  1576. sellAllItems(appId);
  1577. });
  1578. $('.sell_selected').on('click', '*', sellSelectedItems);
  1579. $('.sell_all_cards').on('click', '*', sellAllCards);
  1580. $('.turn_into_gems').on('click', '*', turnSelectedItemsIntoGems);
  1581.  
  1582. }
  1583.  
  1584. $('.inventory_rightnav').prepend(reloadButton);
  1585. $('.reload_inventory').on('click',
  1586. '*',
  1587. function () {
  1588. window.location.reload();
  1589. });
  1590.  
  1591. loadAllInventories().then(function () {
  1592. var updateInventoryPrices = function () {
  1593. setInventoryPrices(getInventoryItems());
  1594. };
  1595.  
  1596. // Load after the inventory is loaded.
  1597. updateInventoryPrices();
  1598.  
  1599. $('#inventory_pagecontrols').observe('childlist',
  1600. '*',
  1601. function (record) {
  1602. updateInventoryPrices();
  1603. });
  1604. },
  1605. function () {
  1606. logDOM('Could not retrieve the inventory...');
  1607. });
  1608. }
  1609.  
  1610. // Loads the specified inventories.
  1611. function loadInventories(inventories) {
  1612. return new Promise(function (resolve) {
  1613. inventories.reduce(function (promise, inventory) {
  1614. return promise.then(function () {
  1615. return inventory.LoadCompleteInventory().done(function () { });
  1616. });
  1617. },
  1618. Promise.resolve());
  1619.  
  1620. resolve();
  1621. });
  1622. }
  1623.  
  1624. // Loads all inventories.
  1625. function loadAllInventories() {
  1626. var items = [];
  1627.  
  1628. for (var child in getActiveInventory().m_rgChildInventories) {
  1629. items.push(getActiveInventory().m_rgChildInventories[child]);
  1630. }
  1631. items.push(getActiveInventory());
  1632.  
  1633. return loadInventories(items);
  1634. }
  1635.  
  1636. // Gets the inventory items from the active inventory.
  1637. function getInventoryItems() {
  1638. var arr = [];
  1639.  
  1640. for (var child in getActiveInventory().m_rgChildInventories) {
  1641. for (var key in getActiveInventory().m_rgChildInventories[child].m_rgAssets) {
  1642. var value = getActiveInventory().m_rgChildInventories[child].m_rgAssets[key];
  1643. if (typeof value === 'object') {
  1644. // Merges the description in the normal object, this is done to keep the layout consistent with the market page, which is also flattened.
  1645. Object.assign(value, value.description);
  1646. // Includes the id of the inventory item.
  1647. value['id'] = key;
  1648. arr.push(value);
  1649. }
  1650. }
  1651. }
  1652.  
  1653. // Some inventories (e.g. BattleBlock Theater) do not have child inventories, they have just one.
  1654. for (var key in getActiveInventory().m_rgAssets) {
  1655. var value = getActiveInventory().m_rgAssets[key];
  1656. if (typeof value === 'object') {
  1657. // Merges the description in the normal object, this is done to keep the layout consistent with the market page, which is also flattened.
  1658. Object.assign(value, value.description);
  1659. // Includes the id of the inventory item.
  1660. value['id'] = key;
  1661. arr.push(value);
  1662. }
  1663. }
  1664.  
  1665. return arr;
  1666. }
  1667. }
  1668. //#endregion
  1669.  
  1670. //#region Inventory + Tradeoffer
  1671. if (currentPage == PAGE_INVENTORY || currentPage == PAGE_TRADEOFFER) {
  1672.  
  1673. // Gets the active inventory.
  1674. function getActiveInventory() {
  1675. return g_ActiveInventory;
  1676. }
  1677.  
  1678. // Sets the prices for the items.
  1679. function setInventoryPrices(items) {
  1680. inventoryPriceQueue.kill();
  1681.  
  1682. items.forEach(function (item) {
  1683. if (!item.marketable) {
  1684. return;
  1685. }
  1686.  
  1687. if (!$(item.element).is(":visible"))
  1688. return;
  1689.  
  1690. inventoryPriceQueue.push(item);
  1691. });
  1692. }
  1693.  
  1694. var inventoryPriceQueue = async.queue(function (item, next) {
  1695. inventoryPriceQueueWorker(item,
  1696. false,
  1697. function (success, cached) {
  1698. if (success) {
  1699. setTimeout(function () {
  1700. next();
  1701. },
  1702. cached ? 0 : getRandomInt(1000, 1500));
  1703. } else {
  1704. if (!item.ignoreErrors) {
  1705. item.ignoreErrors = true;
  1706. inventoryPriceQueue.push(item);
  1707. }
  1708.  
  1709. numberOfFailedRequests++;
  1710.  
  1711. var delay = numberOfFailedRequests > 1
  1712. ? getRandomInt(30000, 45000)
  1713. : getRandomInt(1000, 1500);
  1714.  
  1715. if (numberOfFailedRequests > 3)
  1716. numberOfFailedRequests = 0;
  1717.  
  1718. setTimeout(function () {
  1719. next();
  1720. },
  1721. cached ? 0 : delay);
  1722. }
  1723. });
  1724. },
  1725. 1);
  1726.  
  1727. function inventoryPriceQueueWorker(item, ignoreErrors, callback) {
  1728. var priceInfo = getPriceInformationFromItem(item);
  1729.  
  1730. var failed = 0;
  1731. var itemName = item.name || item.description.name;
  1732.  
  1733. // Only get the market orders here, the history is not important to visualize the current prices.
  1734. market.getItemOrdersHistogram(item,
  1735. true,
  1736. function (err, histogram, cachedListings) {
  1737. if (err) {
  1738. logConsole('Failed to get orders histogram for ' + itemName);
  1739.  
  1740. if (err == ERROR_FAILED)
  1741. failed += 1;
  1742. }
  1743.  
  1744. if (failed > 0 && !ignoreErrors) {
  1745. return callback(false, cachedListings);
  1746. }
  1747.  
  1748. var sellPrice = calculateSellPriceBeforeFees(null, histogram, false, 0, 65535);
  1749. var itemPrice = sellPrice == 65535
  1750. ? '∞'
  1751. : (market.getPriceIncludingFees(sellPrice) / 100.0).toFixed(2) + currencySymbol;
  1752.  
  1753. var elementName = (currentPage == PAGE_TRADEOFFER ? '#item' : '#') +
  1754. item.appid +
  1755. '_' +
  1756. item.contextid +
  1757. '_' +
  1758. item.id;
  1759. var element = $(elementName);
  1760.  
  1761. $('.inventory_item_price', element).remove();
  1762. element.append('<span class="inventory_item_price price_' + (sellPrice == 65535 ? 0 : market.getPriceIncludingFees(sellPrice)) + '">' + itemPrice + '</span>');
  1763.  
  1764. return callback(true, cachedListings);
  1765. });
  1766. }
  1767. }
  1768. //#endregion
  1769.  
  1770. //#region Market
  1771. if (currentPage == PAGE_MARKET || currentPage == PAGE_MARKET_LISTING) {
  1772. var marketListingsQueue = async.queue(function (listing, next) {
  1773. marketListingsQueueWorker(listing,
  1774. false,
  1775. function (success, cached) {
  1776. if (success) {
  1777. setTimeout(function () {
  1778. next();
  1779. },
  1780. cached ? 0 : getRandomInt(1000, 1500));
  1781. } else {
  1782. setTimeout(function () {
  1783. marketListingsQueueWorker(listing,
  1784. true,
  1785. function (success, cached) {
  1786. next(); // Go to the next queue item, regardless of success.
  1787. });
  1788. },
  1789. cached ? 0 : getRandomInt(30000, 45000));
  1790. }
  1791. });
  1792. },
  1793. 1);
  1794.  
  1795. marketListingsQueue.drain = function () {
  1796. injectJs(function () {
  1797. g_bMarketWindowHidden = false;
  1798. })
  1799. };
  1800.  
  1801. function marketListingsQueueWorker(listing, ignoreErrors, callback) {
  1802. var asset = g_rgAssets[listing.appid][listing.contextid][listing.assetid];
  1803.  
  1804. // An asset:
  1805. //{
  1806. // "currency" : 0,
  1807. // "appid" : 753,
  1808. // "contextid" : "6",
  1809. // "id" : "4363079664",
  1810. // "classid" : "2228526061",
  1811. // "instanceid" : "0",
  1812. // "amount" : "1",
  1813. // "status" : 2,
  1814. // "original_amount" : "1",
  1815. // "background_color" : "",
  1816. // "icon_url" : "xx",
  1817. // "icon_url_large" : "xxx",
  1818. // "descriptions" : [{
  1819. // "value" : "Their dense, shaggy fur conceals the presence of swams of moogamites, purple scaly skin, and more nipples than one would expect."
  1820. // }
  1821. // ],
  1822. // "tradable" : 1,
  1823. // "owner_actions" : [{
  1824. // "link" : "http://steamcommunity.com/my/gamecards/443880/",
  1825. // "name" : "View badge progress"
  1826. // }, {
  1827. // "link" : "javascript:GetGooValue( '%contextid%', '%assetid%', 443880, 7, 0 )",
  1828. // "name" : "Turn into Gems..."
  1829. // }
  1830. // ],
  1831. // "name" : "Wook",
  1832. // "type" : "Loot Rascals Trading Card",
  1833. // "market_name" : "Wook",
  1834. // "market_hash_name" : "443880-Wook",
  1835. // "market_fee_app" : 443880,
  1836. // "commodity" : 1,
  1837. // "market_tradable_restriction" : 7,
  1838. // "market_marketable_restriction" : 7,
  1839. // "marketable" : 1,
  1840. // "app_icon" : "xxxx",
  1841. // "owner" : 0
  1842. //}
  1843.  
  1844. var market_hash_name = getMarketHashName(asset);
  1845. var appid = listing.appid;
  1846.  
  1847. var listingUI = $(getListingFromLists(listing.listingid).elm);
  1848.  
  1849. var game_name = asset.type;
  1850. var priceLabel = $('.market_listing_price > span:nth-child(1) > span:nth-child(1)', listingUI).text().trim().replace('--', '00');
  1851.  
  1852. // Fixes RUB, which has a dot at the end.
  1853. if (priceLabel[priceLabel.length - 1] === '.' || priceLabel[priceLabel.length - 1] === ",")
  1854. priceLabel = priceLabel.slice(0, -1);
  1855.  
  1856. // For round numbers (e.g., 100 EUR).
  1857. if (priceLabel.indexOf('.') === -1 && priceLabel.indexOf(',') === -1) {
  1858. priceLabel = priceLabel + ',00';
  1859. }
  1860.  
  1861. var price = parseInt(replaceNonNumbers(priceLabel));
  1862.  
  1863. var priceInfo = getPriceInformationFromItem(asset);
  1864. var item = { appid: parseInt(appid), description: { market_hash_name: market_hash_name } };
  1865.  
  1866. var failed = 0;
  1867.  
  1868. market.getPriceHistory(item,
  1869. true,
  1870. function (errorPriceHistory, history, cachedHistory) {
  1871. if (errorPriceHistory) {
  1872. logConsole('Failed to get price history for ' + game_name);
  1873.  
  1874. if (errorPriceHistory == ERROR_FAILED)
  1875. failed += 1;
  1876. }
  1877.  
  1878. market.getItemOrdersHistogram(item,
  1879. true,
  1880. function (errorHistogram, histogram, cachedListings) {
  1881. if (errorHistogram) {
  1882. logConsole('Failed to get orders histogram for ' + game_name);
  1883.  
  1884. if (errorHistogram == ERROR_FAILED)
  1885. failed += 1;
  1886. }
  1887.  
  1888. if (failed > 0 && !ignoreErrors) {
  1889. return callback(false, cachedHistory && cachedListings);
  1890. }
  1891.  
  1892. // Shows the highest buy order price on the market listings.
  1893. // The 'histogram.highest_buy_order' is not reliable as Steam is caching this value, but it gives some idea for older titles/listings.
  1894. var highestBuyOrderPrice = (histogram == null || histogram.highest_buy_order == null
  1895. ? '-'
  1896. : ((histogram.highest_buy_order / 100) + currencySymbol));
  1897. $('.market_table_value > span:nth-child(1) > span:nth-child(1) > span:nth-child(1)',
  1898. listingUI).append(' ➤ <span title="This is likely the highest buy order price.">' +
  1899. highestBuyOrderPrice +
  1900. '</span>');
  1901.  
  1902. logConsole('============================')
  1903. logConsole(JSON.stringify(listing));
  1904. logConsole(game_name + ': ' + asset.name);
  1905. logConsole('Current price: ' + price / 100.0);
  1906.  
  1907. // Calculate two prices here, one without the offset and one with the offset.
  1908. // The price without the offset is required to not relist the item constantly when you have the lowest price (i.e., with a negative offset).
  1909. // The price with the offset should be used for relisting so it will still apply the user-set offset.
  1910.  
  1911. var sellPriceWithoutOffset = calculateSellPriceBeforeFees(history,
  1912. histogram,
  1913. false,
  1914. priceInfo.minPriceBeforeFees,
  1915. priceInfo.maxPriceBeforeFees);
  1916. var sellPriceWithOffset = calculateSellPriceBeforeFees(history,
  1917. histogram,
  1918. true,
  1919. priceInfo.minPriceBeforeFees,
  1920. priceInfo.maxPriceBeforeFees);
  1921.  
  1922. var sellPriceWithoutOffsetWithFees = market.getPriceIncludingFees(sellPriceWithoutOffset);
  1923.  
  1924. logConsole('Calculated price: ' +
  1925. sellPriceWithoutOffsetWithFees / 100.0 +
  1926. ' (' +
  1927. sellPriceWithoutOffset / 100.0 +
  1928. ')');
  1929.  
  1930. listingUI.addClass('price_' + sellPriceWithOffset);
  1931.  
  1932. $('.market_listing_my_price', listingUI).last().prop('title',
  1933. 'The best price is ' + (sellPriceWithoutOffsetWithFees / 100.0) + currencySymbol + '.');
  1934.  
  1935. if (sellPriceWithoutOffsetWithFees < price) {
  1936. logConsole('Sell price is too high.');
  1937.  
  1938. $('.market_listing_my_price', listingUI).last()
  1939. .css('background', COLOR_PRICE_EXPENSIVE);
  1940. listingUI.addClass('overpriced');
  1941.  
  1942. if (getSettingWithDefault(SETTING_RELIST_AUTOMATICALLY) == 1) {
  1943. queueOverpricedItemListing(listing.listingid);
  1944. }
  1945. } else if (sellPriceWithoutOffsetWithFees > price) {
  1946. logConsole('Sell price is too low.');
  1947.  
  1948. $('.market_listing_my_price', listingUI).last().css('background', COLOR_PRICE_CHEAP);
  1949. listingUI.addClass('underpriced');
  1950. } else {
  1951. logConsole('Sell price is fair.');
  1952.  
  1953. $('.market_listing_my_price', listingUI).last().css('background', COLOR_PRICE_FAIR);
  1954. listingUI.addClass('fair');
  1955. }
  1956.  
  1957. return callback(true, cachedHistory && cachedListings);
  1958. });
  1959. });
  1960. }
  1961.  
  1962. var marketOverpricedQueue = async.queue(function (item, next) {
  1963. marketOverpricedQueueWorker(item,
  1964. false,
  1965. function (success) {
  1966. if (success) {
  1967. setTimeout(function () {
  1968. next();
  1969. },
  1970. getRandomInt(1000, 1500));
  1971. } else {
  1972. setTimeout(function () {
  1973. marketOverpricedQueueWorker(item,
  1974. true,
  1975. function (success) {
  1976. next(); // Go to the next queue item, regardless of success.
  1977. });
  1978. },
  1979. getRandomInt(30000, 45000));
  1980. }
  1981. });
  1982. },
  1983. 1);
  1984.  
  1985. function marketOverpricedQueueWorker(item, ignoreErrors, callback) {
  1986. var listingUI = getListingFromLists(item.listing).elm;
  1987.  
  1988. market.removeListing(item.listing,
  1989. function (errorRemove, data) {
  1990. if (!errorRemove) {
  1991. $('.actual_content', listingUI).css('background', COLOR_PENDING);
  1992.  
  1993. setTimeout(function () {
  1994. var baseUrl = $('.header_notification_items').first().attr('href') + 'json/';
  1995. var itemName = $('.market_listing_item_name_link', listingUI).first().attr('href');
  1996. var marketHashNameIndex = itemName.lastIndexOf('/') + 1;
  1997. var marketHashName = itemName.substring(marketHashNameIndex);
  1998. var decodedMarketHashName = decodeURIComponent(itemName.substring(marketHashNameIndex));
  1999. var newAssetId = -1;
  2000.  
  2001. RequestFullInventory(baseUrl + item.appid + "/" + item.contextid + "/", {}, null, null, function (transport) {
  2002. if (transport.responseJSON && transport.responseJSON.success) {
  2003. var inventory = transport.responseJSON.rgInventory;
  2004.  
  2005. for (var child in inventory) {
  2006. if (inventory[child].appid == item.appid &&
  2007. (inventory[child].market_hash_name == decodedMarketHashName ||
  2008. inventory[child].market_hash_name == marketHashName)) {
  2009. newAssetId = child;
  2010. }
  2011. }
  2012.  
  2013. if (newAssetId == -1) {
  2014. $('.actual_content', listingUI).css('background', COLOR_ERROR);
  2015. return callback(false);
  2016. }
  2017.  
  2018. item.assetid = newAssetId;
  2019. market.sellItem(item,
  2020. item.sellPrice,
  2021. function (errorSell) {
  2022. if (!errorSell) {
  2023. $('.actual_content', listingUI).css('background', COLOR_SUCCESS);
  2024.  
  2025. setTimeout(function () { removeListingFromLists(item.listing) }, 3000);
  2026.  
  2027. return callback(true);
  2028. } else {
  2029. $('.actual_content', listingUI).css('background', COLOR_ERROR);
  2030. return callback(false);
  2031. }
  2032. });
  2033.  
  2034. } else {
  2035. $('.actual_content', listingUI).css('background', COLOR_ERROR);
  2036. return callback(false);
  2037. }
  2038. });
  2039. }, getRandomInt(1500, 2500)); // Wait a little to make sure the item is returned to inventory.
  2040. } else {
  2041. $('.actual_content', listingUI).css('background', COLOR_ERROR);
  2042. return callback(false);
  2043. }
  2044. });
  2045. }
  2046.  
  2047. // Queue an overpriced item listing to be relisted.
  2048. function queueOverpricedItemListing(listingid) {
  2049. var assetInfo = getAssetInfoFromListingId(listingid);
  2050. var listingUI = $(getListingFromLists(listingid).elm);
  2051. var price = -1;
  2052.  
  2053. var items = $(listingUI).attr('class').split(' ');
  2054. for (var i in items) {
  2055. if (items[i].toString().includes('price_'))
  2056. price = parseInt(items[i].toString().replace('price_', ''));
  2057. }
  2058.  
  2059. if (price > 0) {
  2060. marketOverpricedQueue.push({
  2061. listing: listingid,
  2062. assetid: assetInfo.assetid,
  2063. contextid: assetInfo.contextid,
  2064. appid: assetInfo.appid,
  2065. sellPrice: price
  2066. });
  2067. }
  2068. }
  2069.  
  2070. var marketRemoveQueue = async.queue(function (listingid, next) {
  2071. marketRemoveQueueWorker(listingid,
  2072. false,
  2073. function (success) {
  2074. if (success) {
  2075. setTimeout(function () {
  2076. next();
  2077. },
  2078. getRandomInt(50, 100));
  2079. } else {
  2080. setTimeout(function () {
  2081. marketRemoveQueueWorker(listingid,
  2082. true,
  2083. function (success) {
  2084. next(); // Go to the next queue item, regardless of success.
  2085. });
  2086. },
  2087. getRandomInt(30000, 45000));
  2088. }
  2089. });
  2090. },
  2091. 10);
  2092.  
  2093. function marketRemoveQueueWorker(listingid, ignoreErrors, callback) {
  2094. var listingUI = getListingFromLists(listingid).elm;
  2095.  
  2096. market.removeListing(listingid,
  2097. function (errorRemove, data) {
  2098. if (!errorRemove) {
  2099. $('.actual_content', listingUI).css('background', COLOR_SUCCESS);
  2100.  
  2101. setTimeout(function () {
  2102. removeListingFromLists(listingid);
  2103.  
  2104. var numberOfListings = marketLists[0].size;
  2105. if (numberOfListings > 0) {
  2106. $('#my_market_selllistings_number').text((numberOfListings).toString());
  2107.  
  2108. // This seems identical to the number of sell listings.
  2109. $('#my_market_activelistings_number').text((numberOfListings).toString());
  2110. }
  2111. },
  2112. 3000);
  2113.  
  2114. return callback(true);
  2115. } else {
  2116. $('.actual_content', listingUI).css('background', COLOR_ERROR);
  2117.  
  2118. return callback(false);
  2119. }
  2120. });
  2121. }
  2122.  
  2123. var marketListingsItemsQueue = async.queue(function (listing, next) {
  2124. $.get(window.location.protocol + '//steamcommunity.com/market/mylistings?count=100&start=' + listing,
  2125. function (data) {
  2126. if (!data || !data.success) {
  2127. next();
  2128. return;
  2129. }
  2130.  
  2131. var myMarketListings = $('#tabContentsMyActiveMarketListingsRows');
  2132.  
  2133. var nodes = $.parseHTML(data.results_html);
  2134. var rows = $('.market_listing_row', nodes);
  2135. myMarketListings.append(rows);
  2136.  
  2137. // g_rgAssets
  2138. MergeWithAssetArray(data.assets); // This is a method from Steam.
  2139.  
  2140. next();
  2141. },
  2142. 'json')
  2143. .fail(function (data) {
  2144. next();
  2145. return;
  2146. });
  2147. },
  2148. 1);
  2149.  
  2150. marketListingsItemsQueue.drain = function () {
  2151. var myMarketListings = $('#tabContentsMyActiveMarketListingsRows');
  2152. myMarketListings.checkboxes('range', true);
  2153.  
  2154. // Sometimes the Steam API is returning duplicate entries (especially during item listing), filter these.
  2155. var seen = {};
  2156. $('.market_listing_row', myMarketListings).each(function () {
  2157. var item_id = $(this).attr('id');
  2158. if (seen[item_id])
  2159. $(this).remove();
  2160. else
  2161. seen[item_id] = true;
  2162.  
  2163. // Remove listings awaiting confirmations, they are already listed separately.
  2164. if ($('.item_market_action_button', this).attr('href').toLowerCase()
  2165. .includes('CancelMarketListingConfirmation'.toLowerCase()))
  2166. $(this).remove();
  2167.  
  2168. // Remove buy order listings, they are already listed separately.
  2169. if ($('.item_market_action_button', this).attr('href').toLowerCase()
  2170. .includes('CancelMarketBuyOrder'.toLowerCase()))
  2171. $(this).remove();
  2172. });
  2173.  
  2174. // Now add the market checkboxes.
  2175. addMarketCheckboxes();
  2176.  
  2177. // Show the listings again, rendering is done.
  2178. $('#market_listings_spinner').remove();
  2179. myMarketListings.show();
  2180.  
  2181. fillMarketListingsQueue();
  2182.  
  2183. injectJs(function () {
  2184. g_bMarketWindowHidden =
  2185. true; // Limits the number of requests made to steam by stopping constant polling of popular listings.
  2186. });
  2187. };
  2188.  
  2189.  
  2190. function fillMarketListingsQueue() {
  2191. $('.market_home_listing_table').each(function (e) {
  2192.  
  2193. // Not for popular / new / recently sold items (bottom of page).
  2194. if ($('.my_market_header', $(this)).length == 0)
  2195. return;
  2196.  
  2197. // Buy orders and listings confirmations are not grouped like the sell listings, add this so pagination works there as well.
  2198. if (!$(this).attr('id')) {
  2199. $(this).attr('id', 'market-listing-' + e);
  2200.  
  2201. $(this).append('<div class="market_listing_see" id="market-listing-container-' + e + '"></div>')
  2202. $('.market_listing_row', $(this)).appendTo($('#market-listing-container-' + e));
  2203. } else {
  2204. $(this).children().last().addClass("market_listing_see");
  2205. }
  2206.  
  2207. addMarketPagination($('.market_listing_see', this).last());
  2208. sortMarketListings($(this), false, false, true);
  2209. });
  2210.  
  2211. // Add the listings to the queue to be checked for the price.
  2212. for (var i = 0; i < marketLists.length; i++) {
  2213. for (var j = 0; j < marketLists[i].items.length; j++) {
  2214. var listingid = replaceNonNumbers(marketLists[i].items[j].values().market_listing_item_name);
  2215. var assetInfo = getAssetInfoFromListingId(listingid);
  2216.  
  2217. marketListingsQueue.push({
  2218. listingid,
  2219. appid: assetInfo.appid,
  2220. contextid: assetInfo.contextid,
  2221. assetid: assetInfo.assetid
  2222. });
  2223. }
  2224. }
  2225. }
  2226.  
  2227.  
  2228. // Gets the asset info (appid/contextid/assetid) based on a listingid.
  2229. function getAssetInfoFromListingId(listingid) {
  2230. var listing = getListingFromLists(listingid);
  2231. if (listing == null) {
  2232. return {};
  2233. }
  2234.  
  2235. var actionButton = $('.item_market_action_button', listing.elm).attr('href');
  2236. // Market buy orders have no asset info.
  2237. if (actionButton == null || actionButton.toLowerCase().includes('cancelmarketbuyorder'))
  2238. return {};
  2239.  
  2240. var itemIds = actionButton.split(',');
  2241. var appid = replaceNonNumbers(itemIds[2]);
  2242. var contextid = replaceNonNumbers(itemIds[3]);
  2243. var assetid = replaceNonNumbers(itemIds[4]);
  2244. return { appid, contextid, assetid };
  2245. }
  2246.  
  2247. // Adds pagination and search options to the market item listings.
  2248. function addMarketPagination(market_listing_see) {
  2249. market_listing_see.addClass('list');
  2250.  
  2251. market_listing_see.before('<ul class="paginationTop pagination"></ul>');
  2252. market_listing_see.after('<ul class="paginationBottom pagination"></ul>');
  2253.  
  2254. $('.market_listing_table_header', market_listing_see.parent())
  2255. .append('<input class="search" id="market_name_search" placeholder="Search..." />');
  2256.  
  2257. var options = {
  2258. valueNames: [
  2259. 'market_listing_game_name', 'market_listing_item_name_link', 'market_listing_price',
  2260. 'market_listing_listed_date', { name: 'market_listing_item_name', attr: 'id' }
  2261. ],
  2262. pagination: [
  2263. {
  2264. name: "paginationTop",
  2265. paginationClass: "paginationTop",
  2266. innerWindow: 100,
  2267. outerWindow: 100,
  2268. left: 100,
  2269. right: 100
  2270. }, {
  2271. name: "paginationBottom",
  2272. paginationClass: "paginationBottom",
  2273. innerWindow: 100,
  2274. outerWindow: 100,
  2275. left: 100,
  2276. right: 100
  2277. }
  2278. ],
  2279. page: parseInt(getSettingWithDefault(SETTING_MARKET_PAGE_COUNT))
  2280. };
  2281.  
  2282. var list = new List(market_listing_see.parent().attr('id'), options);
  2283. marketLists.push(list);
  2284. }
  2285.  
  2286. // Adds checkboxes to market listings.
  2287. function addMarketCheckboxes() {
  2288. $('.market_listing_row').each(function () {
  2289. // Don't add it again, one time is enough.
  2290. if ($('.market_listing_select', this).length == 0) {
  2291. $('.market_listing_cancel_button', $(this)).append('<div class="market_listing_select">' +
  2292. '<input type="checkbox" class="market_select_item"/>' +
  2293. '</div>');
  2294.  
  2295. $('.market_select_item', this).change(function (e) {
  2296. updateMarketSelectAllButton();
  2297. });
  2298. }
  2299. });
  2300. }
  2301.  
  2302. // Process the market listings.
  2303. function processMarketListings() {
  2304. addMarketCheckboxes();
  2305.  
  2306. if (currentPage == PAGE_MARKET) {
  2307. // Load the market listings.
  2308. var currentCount = 0;
  2309. var totalCount = 0;
  2310.  
  2311. if (typeof g_oMyListings !== 'undefined' && g_oMyListings != null && g_oMyListings.m_cTotalCount != null)
  2312. totalCount = g_oMyListings.m_cTotalCount;
  2313. else {
  2314. totalCount = parseInt($('#my_market_selllistings_number').text());
  2315. }
  2316.  
  2317. if (isNaN(totalCount) || totalCount == 0) {
  2318. fillMarketListingsQueue();
  2319. return;
  2320. }
  2321.  
  2322. $('#tabContentsMyActiveMarketListingsRows').html(''); // Clear the default listings.
  2323. $('#tabContentsMyActiveMarketListingsRows').hide(); // Hide all listings until everything has been loaded.
  2324.  
  2325. // Hide Steam's paging controls.
  2326. $('#tabContentsMyActiveMarketListings_ctn').hide();
  2327. $('.market_pagesize_options').hide();
  2328.  
  2329. // Show the spinner so the user knows that something is going on.
  2330. $('.my_market_header').eq(0).append('<div id="market_listings_spinner">' +
  2331. spinnerBlock +
  2332. '<div style="text-align:center">Loading market listings</div>' +
  2333. '</div>');
  2334.  
  2335. while (currentCount < totalCount) {
  2336. marketListingsItemsQueue.push(currentCount);
  2337. currentCount += 100;
  2338. }
  2339. } else {
  2340. // This is on a market item page.
  2341. $('.market_home_listing_table').each(function (e) {
  2342. // Not on 'x requests to buy at y,yy or lower'.
  2343. if ($('#market_buyorder_info_show_details', $(this)).length > 0)
  2344. return;
  2345.  
  2346. $(this).children().last().addClass("market_listing_see");
  2347.  
  2348. addMarketPagination($('.market_listing_see', this).last());
  2349. sortMarketListings($(this), false, false, true);
  2350. });
  2351.  
  2352. $('#tabContentsMyActiveMarketListingsRows > .market_listing_row').each(function () {
  2353. var listingid = $(this).attr('id').replace('mylisting_', '').replace('mybuyorder_', '').replace('mbuyorder_', '');
  2354. var assetInfo = getAssetInfoFromListingId(listingid);
  2355.  
  2356. // There's only one item in the g_rgAssets on a market listing page.
  2357. var existingAsset = null;
  2358. for (var appid in g_rgAssets) {
  2359. for (var contextid in g_rgAssets[appid]) {
  2360. for (var assetid in g_rgAssets[appid][contextid]) {
  2361. existingAsset = g_rgAssets[appid][contextid][assetid];
  2362. break;
  2363. }
  2364. }
  2365. }
  2366.  
  2367. // appid and contextid are identical, only the assetid is different for each asset.
  2368. g_rgAssets[appid][contextid][assetInfo.assetid] = existingAsset;
  2369. marketListingsQueue.push({ listingid, appid: assetInfo.appid, contextid: assetInfo.contextid, assetid: assetInfo.assetid });
  2370. })
  2371. }
  2372. }
  2373.  
  2374. // Update the select/deselect all button on the market.
  2375. function updateMarketSelectAllButton() {
  2376. $('.market_listing_buttons').each(function () {
  2377. var selectionGroup = $(this).parent().parent();
  2378. var invert = $('.market_select_item:checked', selectionGroup).length == $('.market_select_item', selectionGroup).length;
  2379. if ($('.market_select_item', selectionGroup).length == 0) // If there are no items to select, keep it at Select all.
  2380. invert = false;
  2381. $('.select_all > span', selectionGroup).text(invert ? 'Deselect all' : 'Select all');
  2382. });
  2383. }
  2384.  
  2385. // Sort the market listings.
  2386. function sortMarketListings(elem, isPrice, isDate, isName) {
  2387. var list = getListFromContainer(elem);
  2388. if (list == null) {
  2389. console.log('Invalid parameter, could not find a list matching elem.');
  2390. return;
  2391. }
  2392.  
  2393. // Change sort order (asc/desc).
  2394. var nextSort = isPrice ? 1 : (isDate ? 2 : 3);
  2395. var asc = true;
  2396.  
  2397. // (Re)set the asc/desc arrows.
  2398. const arrow_down = '🡻';
  2399. const arrow_up = '🡹';
  2400.  
  2401. $('.market_listing_table_header > span', elem).each(function () {
  2402. if ($(this).hasClass('market_listing_edit_buttons'))
  2403. return;
  2404.  
  2405. if ($(this).text().includes(arrow_up))
  2406. asc = false;
  2407.  
  2408. $(this).text($(this).text().replace(' ' + arrow_down, '').replace(' ' + arrow_up, ''));
  2409. })
  2410.  
  2411. var market_listing_selector;
  2412. if (isPrice) {
  2413. market_listing_selector = $('.market_listing_table_header', elem).children().eq(1);
  2414. } else if (isDate) {
  2415. market_listing_selector = $('.market_listing_table_header', elem).children().eq(2);
  2416. } else if (isName) {
  2417. market_listing_selector = $('.market_listing_table_header', elem).children().eq(3);
  2418. }
  2419. market_listing_selector.text(market_listing_selector.text() + ' ' + (asc ? arrow_up : arrow_down));
  2420.  
  2421. if (list.sort == null)
  2422. return;
  2423.  
  2424. if (isName) {
  2425. list.sort('',
  2426. {
  2427. order: asc ? "asc" : "desc",
  2428. sortFunction:
  2429. function (a, b) {
  2430. if (a.values().market_listing_game_name.toLowerCase()
  2431. .localeCompare(b.values().market_listing_game_name.toLowerCase()) ==
  2432. 0) {
  2433. return a.values().market_listing_item_name_link.toLowerCase()
  2434. .localeCompare(b.values().market_listing_item_name_link.toLowerCase());
  2435. }
  2436. return a.values().market_listing_game_name.toLowerCase()
  2437. .localeCompare(b.values().market_listing_game_name.toLowerCase());
  2438. }
  2439. });
  2440. } else if (isDate) {
  2441. var currentMonth = parseInt(Date.today().toString('M'));
  2442.  
  2443. list.sort('market_listing_listed_date', {
  2444. order: asc ? "asc" : "desc", sortFunction: function (a, b) {
  2445. var firstDate = Date.parse((a.values().market_listing_listed_date).trim());
  2446. var secondDate = Date.parse((b.values().market_listing_listed_date).trim());
  2447.  
  2448. if (firstDate == null || secondDate == null) {
  2449. return 0;
  2450. }
  2451.  
  2452. if (parseInt(firstDate.toString('M')) > currentMonth)
  2453. firstDate = firstDate.addYears(-1);
  2454. if (parseInt(secondDate.toString('M')) > currentMonth)
  2455. secondDate = secondDate.addYears(-1);
  2456.  
  2457. return firstDate.compareTo(secondDate);
  2458. }
  2459. })
  2460. } else if (isPrice) {
  2461. list.sort('market_listing_price', {
  2462. order: asc ? "asc" : "desc", sortFunction: function (a, b) {
  2463. var listingPriceA = $(a.values().market_listing_price).text();
  2464. listingPriceA = listingPriceA.substr(0, listingPriceA.indexOf('('));
  2465. listingPriceA = listingPriceA.replace('--', '00');
  2466.  
  2467. var listingPriceB = $(b.values().market_listing_price).text();
  2468. listingPriceB = listingPriceB.substr(0, listingPriceB.indexOf('('));
  2469. listingPriceB = listingPriceB.replace('--', '00');
  2470.  
  2471. var firstPrice = parseInt(replaceNonNumbers(listingPriceA));
  2472. var secondPrice = parseInt(replaceNonNumbers(listingPriceB));
  2473.  
  2474. return firstPrice - secondPrice;
  2475. }
  2476. })
  2477. }
  2478. }
  2479.  
  2480. function getListFromContainer(group) {
  2481. for (var i = 0; i < marketLists.length; i++) {
  2482. if (group.attr('id') == $(marketLists[i].listContainer).attr('id'))
  2483. return marketLists[i];
  2484. }
  2485. }
  2486.  
  2487. function getListingFromLists(listingid) {
  2488. // Sometimes listing ids are contained in multiple lists (?), use the last one available as this is the one we're most likely interested in.
  2489. for (var i = marketLists.length - 1; i >= 0; i--) {
  2490. var values = marketLists[i].get("market_listing_item_name", 'mylisting_' + listingid + '_name');
  2491. if (values != null && values.length > 0) {
  2492. return values[0];
  2493. }
  2494.  
  2495. values = marketLists[i].get("market_listing_item_name", 'mbuyorder_' + listingid + '_name');
  2496. if (values != null && values.length > 0) {
  2497. return values[0];
  2498. }
  2499. }
  2500.  
  2501.  
  2502. }
  2503.  
  2504. function removeListingFromLists(listingid) {
  2505. for (var i = 0; i < marketLists.length; i++) {
  2506. marketLists[i].remove("market_listing_item_name", 'mylisting_' + listingid + '_name');
  2507. marketLists[i].remove("market_listing_item_name", 'mbuyorder_' + listingid + '_name');
  2508. }
  2509. }
  2510.  
  2511. // Initialize the market UI.
  2512. function initializeMarketUI() {
  2513. // Sell orders.
  2514. $('.my_market_header').first().append(
  2515. '<div class="market_listing_buttons">' +
  2516. '<a class="item_market_action_button item_market_action_button_green select_all market_listing_button">' +
  2517. '<span class="item_market_action_button_contents" style="text-transform:none">Select all</span>' +
  2518. '</a>' +
  2519. '<span class="separator-small"></span>' +
  2520. '<a class="item_market_action_button item_market_action_button_green remove_selected market_listing_button">' +
  2521. '<span class="item_market_action_button_contents" style="text-transform:none">Remove selected</span>' +
  2522. '</a>' +
  2523. '<a class="item_market_action_button item_market_action_button_green relist_selected market_listing_button market_listing_button_right">' +
  2524. '<span class="item_market_action_button_contents" style="text-transform:none">Relist selected</span>' +
  2525. '</a>' +
  2526. '<span class="separator-small"></span>' +
  2527. '<a class="item_market_action_button item_market_action_button_green relist_overpriced market_listing_button market_listing_button_right">' +
  2528. '<span class="item_market_action_button_contents" style="text-transform:none">Relist overpriced</span>' +
  2529. '</a>' +
  2530. '<span class="separator-small"></span>' +
  2531. '<a class="item_market_action_button item_market_action_button_green select_overpriced market_listing_button market_listing_button_right">' +
  2532. '<span class="item_market_action_button_contents" style="text-transform:none">Select overpriced</span>' +
  2533. '</a>' +
  2534. '</div>');
  2535.  
  2536. // Listings confirmations and buy orders.
  2537. $('.my_market_header').slice(1).append(
  2538. '<div class="market_listing_buttons">' +
  2539. '<a class="item_market_action_button item_market_action_button_green select_all market_listing_button">' +
  2540. '<span class="item_market_action_button_contents" style="text-transform:none">Select all</span>' +
  2541. '</a>' +
  2542. '<span class="separator-large"></span>' +
  2543. '<a class="item_market_action_button item_market_action_button_green remove_selected market_listing_button">' +
  2544. '<span class="item_market_action_button_contents" style="text-transform:none">Remove selected</span>' +
  2545. '</a>' +
  2546. '</div>');
  2547.  
  2548. $('.market_listing_table_header').on('click', 'span', function () {
  2549. if ($(this).hasClass('market_listing_edit_buttons') || $(this).hasClass('item_market_action_button_contents'))
  2550. return;
  2551.  
  2552. var isPrice = $('.market_listing_table_header', $(this).parent().parent()).children().eq(1).text() == $(this).text();
  2553. var isDate = $('.market_listing_table_header', $(this).parent().parent()).children().eq(2).text() == $(this).text();
  2554. var isName = $('.market_listing_table_header', $(this).parent().parent()).children().eq(3).text() == $(this).text();
  2555.  
  2556. sortMarketListings($(this).parent().parent(), isPrice, isDate, isName);
  2557. });
  2558.  
  2559. $('.select_all').on('click', '*', function () {
  2560. var selectionGroup = $(this).parent().parent().parent().parent();
  2561. var marketList = getListFromContainer(selectionGroup);
  2562.  
  2563. var invert = $('.market_select_item:checked', selectionGroup).length == $('.market_select_item', selectionGroup).length;
  2564.  
  2565. for (var i = 0; i < marketList.items.length; i++) {
  2566. $('.market_select_item', marketList.items[i].elm).prop('checked', !invert);
  2567. }
  2568.  
  2569. updateMarketSelectAllButton();
  2570. });
  2571.  
  2572.  
  2573. $('#market_removelisting_dialog_accept').on('click', '*', function () {
  2574. // This is when a user removed an item through the Remove/Cancel button.
  2575. // Ideally, it should remove this item from the list (instead of just the UI element which Steam does), but I'm not sure how to get the current item yet.
  2576. window.location.reload();
  2577. });
  2578.  
  2579. $('.select_overpriced').on('click', '*', function () {
  2580. var selectionGroup = $(this).parent().parent().parent().parent();
  2581. var marketList = getListFromContainer(selectionGroup);
  2582.  
  2583. for (var i = 0; i < marketList.items.length; i++) {
  2584. if ($(marketList.items[i].elm).hasClass('overpriced')) {
  2585. $('.market_select_item', marketList.items[i].elm).prop('checked', true);
  2586. }
  2587. }
  2588.  
  2589. $('.market_listing_row', selectionGroup).each(function (index) {
  2590. if ($(this).hasClass('overpriced'))
  2591. $('.market_select_item', $(this)).prop('checked', true);
  2592. });
  2593.  
  2594. updateMarketSelectAllButton();
  2595. });
  2596.  
  2597. $('.remove_selected').on('click', '*', function () {
  2598. var selectionGroup = $(this).parent().parent().parent().parent();
  2599. var marketList = getListFromContainer(selectionGroup);
  2600.  
  2601. for (var i = 0; i < marketList.items.length; i++) {
  2602. if ($('.market_select_item', $(marketList.items[i].elm)).prop('checked')) {
  2603. var listingid = replaceNonNumbers(marketList.items[i].values().market_listing_item_name);
  2604. marketRemoveQueue.push(listingid);
  2605. }
  2606. }
  2607. });
  2608.  
  2609. $('.market_relist_auto').change(function () {
  2610. setSetting(SETTING_RELIST_AUTOMATICALLY, $('.market_relist_auto').is(":checked") ? 1 : 0);
  2611. });
  2612.  
  2613. $('.relist_overpriced').on('click', '*', function () {
  2614. var selectionGroup = $(this).parent().parent().parent().parent();
  2615. var marketList = getListFromContainer(selectionGroup);
  2616.  
  2617. for (var i = 0; i < marketList.items.length; i++) {
  2618. if ($(marketList.items[i].elm).hasClass('overpriced')) {
  2619. var listingid = replaceNonNumbers(marketList.items[i].values().market_listing_item_name);
  2620. queueOverpricedItemListing(listingid);
  2621. }
  2622. }
  2623. });
  2624.  
  2625. $('.relist_selected').on('click', '*', function () {
  2626. var selectionGroup = $(this).parent().parent().parent().parent();
  2627. var marketList = getListFromContainer(selectionGroup);
  2628.  
  2629. for (var i = 0; i < marketList.items.length; i++) {
  2630. if ($(marketList.items[i].elm).hasClass('overpriced') && $('.market_select_item', $(marketList.items[i].elm)).prop('checked')) {
  2631. var listingid = replaceNonNumbers(marketList.items[i].values().market_listing_item_name);
  2632. queueOverpricedItemListing(listingid);
  2633. }
  2634. }
  2635. });
  2636.  
  2637. $('#see_settings').remove();
  2638. $('#global_action_menu').prepend('<span id="see_settings"><a href="javascript:void(0)">⬖ Steam Economy Enhancer</a></span>');
  2639. $('#see_settings').on('click', '*', () => openSettings());
  2640.  
  2641. processMarketListings();
  2642. }
  2643. }
  2644. //#endregion
  2645.  
  2646. //#region Tradeoffers
  2647. function sumTradeOfferAssets(assets, user) {
  2648. var total = {};
  2649. var totalPrice = 0;
  2650. for (var i = 0; i < assets.length; i++) {
  2651. var rgItem = user.findAsset(assets[i].appid, assets[i].contextid, assets[i].assetid);
  2652.  
  2653. var text = '';
  2654. if (rgItem != null) {
  2655. if (rgItem.element) {
  2656. var inventoryPriceElements = $('.inventory_item_price', rgItem.element);
  2657. if (inventoryPriceElements.length) {
  2658. var firstPriceElement = inventoryPriceElements[0];
  2659. var classes = $(firstPriceElement).attr('class').split(' ');
  2660. for (var c in classes) {
  2661. if (classes[c].toString().includes('price_')) {
  2662. var price = parseInt(classes[c].toString().replace('price_', ''));
  2663. totalPrice += price;
  2664. }
  2665. }
  2666.  
  2667. }
  2668. }
  2669.  
  2670. if (rgItem.original_amount != null && rgItem.amount != null) {
  2671. var originalAmount = parseInt(rgItem.original_amount);
  2672. var currentAmount = parseInt(rgItem.amount);
  2673. var usedAmount = originalAmount - currentAmount;
  2674. text += usedAmount.toString() + 'x ';
  2675. }
  2676.  
  2677. text += rgItem.name;
  2678.  
  2679. if (rgItem.type != null && rgItem.type.length > 0) {
  2680. text += ' (' + rgItem.type + ')';
  2681. }
  2682. }
  2683. else
  2684. text = 'Unknown Item';
  2685.  
  2686. if (text in total)
  2687. total[text] = total[text] + 1;
  2688. else
  2689. total[text] = 1;
  2690. }
  2691.  
  2692. var sortable = [];
  2693. for (var item in total)
  2694. sortable.push([item, total[item]])
  2695.  
  2696. sortable.sort(function (a, b) {
  2697. return a[1] - b[1];
  2698. }).reverse();
  2699.  
  2700. var povtor = [];
  2701. var sets = 0;
  2702. for (var i = 0; i < sortable.length; i++) {
  2703. var gameName;
  2704. gameName = sortable[i][0].substring(sortable[i][0].lastIndexOf("("),sortable[i][0].lastIndexOf(")"));
  2705. if(povtor.indexOf(gameName) == -1){
  2706. povtor.push(gameName);
  2707. sets += sortable[i][1];
  2708. }
  2709. };
  2710. var totalText = '<strong>SETS = '+sets+'<br/>'+'Number of items: ' + sortable.length + '<br/><br/></strong>';
  2711.  
  2712. //for (var i = 0; i < sortable.length; i++) {
  2713. // totalText += sortable[i][1] + 'x ' + sortable[i][0] + '<br/>';
  2714. //}
  2715.  
  2716. return totalText;
  2717. }
  2718.  
  2719. function initializeTradeOfferUI() {
  2720.  
  2721. $('.trade_right > div > div > div > .trade_item_box').observe('childlist subtree', function (record) {
  2722. $('#trade_offer_your_sum').remove();
  2723. $('#trade_offer_their_sum').remove();
  2724.  
  2725. var your_sum = sumTradeOfferAssets(g_rgCurrentTradeStatus.me.assets, UserYou);
  2726. var their_sum = sumTradeOfferAssets(g_rgCurrentTradeStatus.them.assets, UserThem);
  2727.  
  2728. $('div.offerheader:nth-child(1) > div:nth-child(3)').append('<div class="trade_offer_sum" id="trade_offer_your_sum">' + your_sum + '</div>');
  2729. $('div.offerheader:nth-child(3) > div:nth-child(3)').append('<div class="trade_offer_sum" id="trade_offer_their_sum">' + their_sum + '</div>');
  2730. });
  2731.  
  2732.  
  2733. // This only works with a new trade offer.
  2734. if (!window.location.href.includes('tradeoffer/new'))
  2735. return;
  2736.  
  2737. var updateInventoryPrices = function () {
  2738. var tradeOfferItems = [];
  2739. for (var i = 0; i < getActiveInventory().rgItemElements.length; i++) {
  2740. tradeOfferItems.push(getActiveInventory().rgItemElements[i].rgItem);
  2741. }
  2742.  
  2743. setInventoryPrices(tradeOfferItems);
  2744. }
  2745.  
  2746. $('#inventory_pagecontrols').observe('childlist', '*', function (record) {
  2747. updateInventoryPrices();
  2748. });
  2749.  
  2750. $('#inventory_displaycontrols').append(
  2751. '<br/>' +
  2752. '<div class="trade_offer_buttons">' +
  2753. '<a class="item_market_action_button item_market_action_button_green select_all" style="margin-top:1px">' +
  2754. '<span class="item_market_action_button_contents" style="text-transform:none">Select all from page</span>' +
  2755. '</a>' +
  2756. '</div>');
  2757.  
  2758. $('.select_all').on('click', '*', function () {
  2759. $('.inventory_ctn:visible > .inventory_page:visible > .itemHolder:visible').delayedEach(250, function (i, it) {
  2760. var item = it.rgItem;
  2761. if (item.is_stackable)
  2762. return;
  2763.  
  2764. if (!item.tradable)
  2765. return;
  2766.  
  2767. MoveItemToTrade(it);
  2768. });
  2769. });
  2770. }
  2771. //#endregion
  2772.  
  2773. //#region Settings
  2774. function openSettings() {
  2775. var price_options = $('<div id="price_options">' +
  2776. '<div style="margin-bottom:6px;">' +
  2777. 'Calculate prices as the:&nbsp;<select class="price_option_input" style="background-color: black;color: white;border: transparent;" id="' + SETTING_PRICE_ALGORITHM + '">' +
  2778. '<option value="1"' + (getSettingWithDefault(SETTING_PRICE_ALGORITHM) == 1 ? 'selected="selected"' : '') + '>maximum of the average (12 hours) and lowest listing</option>' +
  2779. '<option value="2" ' + (getSettingWithDefault(SETTING_PRICE_ALGORITHM) == 2 ? 'selected="selected"' : '') + '>lowest listing</option>' +
  2780. '</select>' +
  2781. '<br/>' +
  2782. '</div>' +
  2783. '<div style="margin-bottom:6px;">' +
  2784. 'The value to add to the calculated price (minimum and maximum are respected):&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_PRICE_OFFSET + '" value=' + getSettingWithDefault(SETTING_PRICE_OFFSET) + '>' +
  2785. '<br/>' +
  2786. '</div>' +
  2787. '<div style="margin-top:6px">' +
  2788. 'Use the second lowest listing when the lowest listing has a low quantity:&nbsp;<input class="price_option_input" style="background-color: black;color: white;border: transparent;" type="checkbox" id="' + SETTING_PRICE_IGNORE_LOWEST_Q + '" ' + (getSettingWithDefault(SETTING_PRICE_IGNORE_LOWEST_Q) == 1 ? 'checked=""' : '') + '>' +
  2789. '<br/>' +
  2790. '</div>' +
  2791. '<div style="margin-top:24px">' +
  2792. '<div style="margin-bottom:6px;">' +
  2793. 'Minimum:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MIN_NORMAL_PRICE + '" value=' + getSettingWithDefault(SETTING_MIN_NORMAL_PRICE) + '>&nbsp;' +
  2794. 'and maximum:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MAX_NORMAL_PRICE + '" value=' + getSettingWithDefault(SETTING_MAX_NORMAL_PRICE) + '>&nbsp;price for normal cards' +
  2795. '<br/>' +
  2796. '</div>' +
  2797. '<div style="margin-bottom:6px;">' +
  2798. 'Minimum:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MIN_FOIL_PRICE + '" value=' + getSettingWithDefault(SETTING_MIN_FOIL_PRICE) + '>&nbsp;' +
  2799. 'and maximum:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MAX_FOIL_PRICE + '" value=' + getSettingWithDefault(SETTING_MAX_FOIL_PRICE) + '>&nbsp;price for foil cards' +
  2800. '<br/>' +
  2801. '</div>' +
  2802. '<div style="margin-bottom:6px;">' +
  2803. 'Minimum:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MIN_MISC_PRICE + '" value=' + getSettingWithDefault(SETTING_MIN_MISC_PRICE) + '>&nbsp;' +
  2804. 'and maximum:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MAX_MISC_PRICE + '" value=' + getSettingWithDefault(SETTING_MAX_MISC_PRICE) + '>&nbsp;price for other items' +
  2805. '<br/>' +
  2806. '</div>' +
  2807. '<div style="margin-top:24px;margin-bottom:6px;">' +
  2808. 'Market items per page:&nbsp;<input class="price_option_input price_option_price" style="background-color: black;color: white;border: transparent;" type="number" step="0.01" id="' + SETTING_MARKET_PAGE_COUNT + '" value=' + getSettingWithDefault(SETTING_MARKET_PAGE_COUNT) + '>' +
  2809. '<br/>' +
  2810. '<div style="margin-top:6px;">' +
  2811. 'Automatically relist overpriced market listings (slow on large inventories):&nbsp;<input id="' + SETTING_RELIST_AUTOMATICALLY + '" class="market_relist_auto" type="checkbox" ' + (getSettingWithDefault(SETTING_RELIST_AUTOMATICALLY) == 1 ? 'checked=""' : '') + '>' +
  2812. '</label>' +
  2813. '</div>' +
  2814. '</div>' +
  2815. '</div>');
  2816.  
  2817. var dialog = ShowConfirmDialog('Steam Economy Enhancer', price_options).done(function () {
  2818. setSetting(SETTING_MIN_NORMAL_PRICE, $('#' + SETTING_MIN_NORMAL_PRICE, price_options).val());
  2819. setSetting(SETTING_MAX_NORMAL_PRICE, $('#' + SETTING_MAX_NORMAL_PRICE, price_options).val());
  2820. setSetting(SETTING_MIN_FOIL_PRICE, $('#' + SETTING_MIN_FOIL_PRICE, price_options).val());
  2821. setSetting(SETTING_MAX_FOIL_PRICE, $('#' + SETTING_MAX_FOIL_PRICE, price_options).val());
  2822. setSetting(SETTING_MIN_MISC_PRICE, $('#' + SETTING_MIN_MISC_PRICE, price_options).val());
  2823. setSetting(SETTING_MAX_MISC_PRICE, $('#' + SETTING_MAX_MISC_PRICE, price_options).val());
  2824. setSetting(SETTING_PRICE_OFFSET, $('#' + SETTING_PRICE_OFFSET, price_options).val());
  2825. setSetting(SETTING_PRICE_ALGORITHM, $('#' + SETTING_PRICE_ALGORITHM, price_options).val());
  2826. setSetting(SETTING_MARKET_PAGE_COUNT, $('#' + SETTING_MARKET_PAGE_COUNT, price_options).val());
  2827. setSetting(SETTING_RELIST_AUTOMATICALLY, $('#' + SETTING_RELIST_AUTOMATICALLY, price_options).prop('checked') ? 1 : 0);
  2828. setSetting(SETTING_PRICE_IGNORE_LOWEST_Q, $('#' + SETTING_PRICE_IGNORE_LOWEST_Q, price_options).prop('checked') ? 1 : 0);
  2829.  
  2830. window.location.reload();
  2831. });
  2832. }
  2833. //#endregion
  2834.  
  2835. //#region UI
  2836. injectCss('.ui-selected { outline: 1px groove #FFFFFF; } ' +
  2837. '#logger { color: #767676; font-size: 12px;margin-top:16px; max-height: 200px; overflow-y: auto; }' +
  2838. '.trade_offer_sum { color: #767676; font-size: 12px;margin-top:8px; }' +
  2839. '.trade_offer_buttons { margin-top: 12px; }' +
  2840. '.market_commodity_orders_table { font-size:12px; font-family: "Motiva Sans", Sans-serif; font-weight: 300; }' +
  2841. '.market_commodity_orders_table th { padding-left: 10px; }' +
  2842. '#listings_group { display: flex; justify-content: space-between; margin-bottom: 8px; }' +
  2843. '#listings_sell { text-align: right; color: #589328; font-weight:600; }' +
  2844. '#listings_buy { text-align: right; color: #589328; font-weight:600; }' +
  2845. '.market_listing_my_price { height: 50px; padding-right:6px; }' +
  2846. '.market_listing_edit_buttons.actual_content { width:276px; transition-property: background-color, border-color; transition-timing-function: linear; transition-duration: 0.5s;}' +
  2847. '.market_listing_buttons { margin-top: 6px; background: rgba(0, 0, 0, 0.4); padding: 5px 0px 1px 0px; }' +
  2848. '.market_listing_button { margin-right: 4px; }' +
  2849. '.market_listing_button_right { float:right; }' +
  2850. '.market_listing_button:first-child { margin-left: 4px; }' +
  2851. '.market_listing_label_right { float:right; font-size:12px; margin-top:1px; }' +
  2852. '.market_listing_select { position: absolute; top: 16px;right: 10px; display: flex; }' +
  2853. '#market_listing_relist { vertical-align: middle; position: relative; bottom: -1px; right: 2px; }' +
  2854. '.pick_and_sell_button > a { vertical-align: middle; }' +
  2855. '.market_relist_auto { margin-bottom: 8px; }' +
  2856. '.market_relist_auto_label { margin-right: 6px; }' +
  2857. '.quick_sell { margin-right: 4px; }' +
  2858. '.spinner{margin:10px auto;width:50px;height:40px;text-align:center;font-size:10px;}.spinner > div{background-color:#ccc;height:100%;width:6px;display:inline-block;-webkit-animation:sk-stretchdelay 1.2s infinite ease-in-out;animation:sk-stretchdelay 1.2s infinite ease-in-out}.spinner .rect2{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.spinner .rect3{-webkit-animation-delay:-1s;animation-delay:-1s}.spinner .rect4{-webkit-animation-delay:-.9s;animation-delay:-.9s}.spinner .rect5{-webkit-animation-delay:-.8s;animation-delay:-.8s}@-webkit-keyframes sk-stretchdelay{0%,40%,100%{-webkit-transform:scaleY(0.4)}20%{-webkit-transform:scaleY(1.0)}}@keyframes sk-stretchdelay{0%,40%,100%{transform:scaleY(0.4);-webkit-transform:scaleY(0.4)}20%{transform:scaleY(1.0);-webkit-transform:scaleY(1.0)}}' +
  2859. '#market_name_search { float: right; background: rgba(0, 0, 0, 0.25); color: white; border: none;height: 25px; padding-left: 6px;}' +
  2860. '.price_option_price { width: 100px }' +
  2861. '#see_settings { background: #26566c; margin-right: 10px; height: 21px; line-height:21px; display:inline-block; padding: 0px 6px; }' +
  2862. '.inventory_item_price { top: 0px;position: absolute;right: 0;background: #3571a5;padding: 2px;color: white; font-size:11px; border: 1px solid #666666;}' +
  2863. '.separator-large {display:inline-block;width:6px;}' +
  2864. '.separator-small {display:inline-block;width:1px;}' +
  2865. '.pagination { padding-left: 0px; }' +
  2866. '.pagination li { display:inline-block; padding: 5px 10px;background: rgba(255, 255, 255, 0.10); margin-right: 6px; border: 1px solid #666666; }' +
  2867. '.pagination li.active { background: rgba(255, 255, 255, 0.25); }');
  2868.  
  2869. $(document).ready(function () {
  2870. // Make sure the user is logged in, there's not much we can do otherwise.
  2871. if (!isLoggedIn) {
  2872. return;
  2873. }
  2874.  
  2875. if (currentPage == PAGE_INVENTORY) {
  2876. initializeInventoryUI();
  2877. }
  2878.  
  2879. if (currentPage == PAGE_MARKET || currentPage == PAGE_MARKET_LISTING) {
  2880. initializeMarketUI();
  2881. }
  2882.  
  2883. if (currentPage == PAGE_TRADEOFFER) {
  2884. initializeTradeOfferUI();
  2885. }
  2886. });
  2887.  
  2888. function injectCss(css) {
  2889. var head, style;
  2890. head = document.getElementsByTagName('head')[0];
  2891. if (!head) {
  2892. return;
  2893. }
  2894. style = document.createElement('style');
  2895. style.type = 'text/css';
  2896. style.innerHTML = css;
  2897. head.appendChild(style);
  2898. }
  2899.  
  2900. function injectJs(js) {
  2901. var script = document.createElement('script');
  2902. script.setAttribute("type", "application/javascript");
  2903. script.textContent = '(' + js + ')();';
  2904. document.body.appendChild(script);
  2905. document.body.removeChild(script);
  2906. }
  2907.  
  2908. $.fn.delayedEach = function (timeout, callback, continuous) {
  2909. var $els, iterator;
  2910.  
  2911. $els = this;
  2912. iterator = function (index) {
  2913. var cur;
  2914.  
  2915. if (index >= $els.length) {
  2916. if (!continuous) {
  2917. return;
  2918. }
  2919. index = 0;
  2920. }
  2921.  
  2922. cur = $els[index];
  2923. callback.call(cur, index, cur);
  2924.  
  2925. setTimeout(function () {
  2926. iterator(++index);
  2927. }, timeout);
  2928. };
  2929.  
  2930. iterator(0);
  2931. };
  2932.  
  2933. String.prototype.replaceAll = function (search, replacement) {
  2934. var target = this;
  2935. return target.replace(new RegExp(search, 'g'), replacement);
  2936. };
  2937. //#endregion
  2938. })(jQuery, async);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement