Bundle Helper

Add tools for many bundle sites.

当前为 2017-05-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Bundle Helper
  3. // @namespace iFantz7E.BundleHelper
  4. // @version 0.17
  5. // @description Add tools for many bundle sites.
  6. // @match *://cubicbundle.com/*
  7. // @match *://dailyindiegame.com/*
  8. // @match *://forums.steampowered.com/forums/showthread.php?*
  9. // @match *://gogobundle.com/latest/bundles/*
  10. // @match *://groupees.com/*
  11. // @match *://otakumaker.com/*
  12. // @match *://steamcommunity.com/*/home*
  13. // @match *://steamcommunity.com/groups/*/announcements*
  14. // @match *://steamcompanion.com/gifts/*
  15. // @match *://steamground.com/*
  16. // @match *://store.steampowered.com/account/notinterested/*
  17. // @match *://store.steampowered.com/app/*
  18. // @match *://store.steampowered.com/widget/*
  19. // @match *://whosgamingnow.net/*
  20. // @match *://www.bunchkeys.com/*
  21. // @match *://www.bundlekings.com/*
  22. // @match *://www.bundlestars.com/*/bundle/*
  23. // @match *://www.bundlestars.com/*/promotions/*
  24. // @match *://www.dailyindiegame.com/*
  25. // @match *://www.gamebundle.com/*
  26. // @match *://www.hrkgame.com/*
  27. // @match *://www.humblebundle.com/*
  28. // @match *://www.indiegala.com/*
  29. // @match *://www.orlygift.com/*
  30. // @match *://www.otakumaker.com/*
  31. // @match *://www.reddit.com/r/*/comments/*
  32. // @match *://www.superduperbundle.com/*
  33. // @run-at document-start
  34. // @grant GM_addStyle
  35. // @grant GM_xmlhttpRequest
  36. // @connect store.steampowered.com
  37. // @connect www.hrkgame.com
  38. // @connect www.bundlestars.com
  39. // @connect www.steamgifts.com
  40. // @icon http://store.steampowered.com/favicon.ico
  41. // @copyright 2016, 7-elephant
  42. // ==/UserScript==
  43.  
  44. //// Connect to store.steampowered.com to get owner info
  45. //// Connect to www.hrkgame.com and www.bundlestars.com to get Steam ID of each products
  46. //// Connect to www.steamgifts.com to get bundle threads
  47.  
  48. (function ()
  49. {
  50. function attachOnLoad(callback)
  51. {
  52. window.addEventListener("load", function (e)
  53. {
  54. callback();
  55. });
  56. }
  57.  
  58. function attachOnReady(callback)
  59. {
  60. document.addEventListener("DOMContentLoaded", function (e)
  61. {
  62. callback();
  63. });
  64. }
  65.  
  66. function insertBeforeElement(newNode, referenceNode)
  67. {
  68. referenceNode.parentNode.insertBefore(newNode, referenceNode);
  69. }
  70.  
  71. function insertAfterElement(newNode, referenceNode)
  72. {
  73. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  74. }
  75.  
  76. function reload()
  77. {
  78. var curHref = window.location.href;
  79. var posHashtag = curHref.indexOf("#");
  80. if (posHashtag > -1)
  81. {
  82. window.location = curHref.substr(0, posHashtag);
  83. }
  84. else
  85. {
  86. window.location = curHref;
  87. }
  88. }
  89. function getQueryByName(name, url)
  90. {
  91. if (url == null)
  92. {
  93. url = location == null ? "" : location.search;
  94. }
  95. name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  96. var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
  97. var results = regex.exec(url);
  98. var retVal = "";
  99. if (results != null)
  100. {
  101. retVal = results[1].replace(/\+/g, " ");
  102. try
  103. {
  104. retVal = decodeURIComponent(retVal);
  105. }
  106. catch (ex)
  107. {
  108. console.error("getQueryByName", ex.message);
  109. }
  110. }
  111. return retVal;
  112. }
  113.  
  114. function clickToSelect(ele)
  115. {
  116. if (ele != null)
  117. {
  118. var range = document.createRange();
  119. range.setStartBefore(ele.firstChild);
  120. range.setEndAfter(ele.lastChild);
  121.  
  122. var sel = window.getSelection();
  123. sel.removeAllRanges();
  124. sel.addRange(range);
  125. }
  126. }
  127. function normalizeArray(arr)
  128. {
  129. arr = arr.filter(function(elem, index, self)
  130. {
  131. return index == self.indexOf(elem);
  132. });
  133. return arr;
  134. }
  135. function randNum(min, max)
  136. {
  137. return Math.round(Math.random() * (max - min) + min);
  138. }
  139. function markOwned(query, markOwnedCallback)
  140. {
  141. var rgxId = /[0-9]{3,}/g;
  142. var rgxApp = /:\/\/((store\.steampowered|steamcommunity)\.com\/app|cdn.akamai.steamstatic.com\/steam\/apps)\/[0-9]+/i;
  143. var rgxSub = /:\/\/store\.steampowered\.com\/sub\/[0-9]+/i;
  144. GM_xmlhttpRequest(
  145. {
  146. method: "GET",
  147. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + randNum(1000, 9999),
  148. onload: function(response)
  149. {
  150. var dataRes = JSON.parse(response.responseText);
  151. var countOwned = [0, 0];
  152. var countAll = [0, 0];
  153. if (typeof dataRes["rgOwnedApps"] !== "undefined"
  154. && typeof dataRes["rgOwnedPackages"] !== "undefined"
  155. && typeof dataRes["rgIgnoredApps"] !== "undefined")
  156. {
  157. var eleApps = document.querySelectorAll(query);
  158. for (var i = 0; i < eleApps.length; i++)
  159. {
  160. var attrHref = eleApps[i].getAttribute("href") || eleApps[i].getAttribute("src");
  161. var ids = attrHref.match(rgxId);
  162. if (ids != null)
  163. {
  164. var valId = parseInt(ids[0]);
  165. if (rgxApp.test(attrHref))
  166. {
  167. if (dataRes["rgOwnedApps"].indexOf(valId) > -1)
  168. {
  169. markOwnedCallback(eleApps[i]);
  170. countOwned[0]++;
  171. }
  172. else
  173. {
  174. console.log("App: not owned - http://store.steampowered.com/app/" + valId + "/");
  175. }
  176. countAll[0]++;
  177. }
  178. else if (rgxSub.test(attrHref))
  179. {
  180. if (dataRes["rgOwnedPackages"].indexOf(valId) > -1)
  181. {
  182. markOwnedCallback(eleApps[i]);
  183. countOwned[1]++;
  184. }
  185. else
  186. {
  187. console.log("Sub: not owned - http://store.steampowered.com/sub/" + valId + "/");
  188. }
  189. countAll[1]++;
  190. }
  191. }
  192. }
  193. }
  194. var diff = countAll[0] - countOwned[0];
  195. console.log("App: " + countOwned[0] + "/" + countAll[0] + (diff > 10 ? " Diff: " + diff : ""));
  196. console.log("Sub: " + countOwned[1] + "/" + countAll[1]);
  197. } // End onload
  198. });
  199. }
  200. function markOwned_old(query, getLabelCallback)
  201. {
  202. var apps = [];
  203. var eleApps = document.querySelectorAll(query);
  204. for (var i = 0; i < eleApps.length; i++)
  205. {
  206. var app = /[0-9]+/.exec(eleApps[i].getAttribute("href"));
  207. if (app != null)
  208. {
  209. apps.push(app[0]);
  210. }
  211. }
  212. apps = apps.filter(function(elem, index, self)
  213. {
  214. return index == self.indexOf(elem);
  215. });
  216. console.log("Apps: " + apps.length);
  217. var appAll = apps.join(",");
  218. GM_xmlhttpRequest(
  219. {
  220. method: "GET",
  221. headers:
  222. {
  223. "Cache-Control": "max-age=0"
  224. },
  225. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  226. onload: function(response)
  227. {
  228. var dataRes = JSON.parse(response.responseText);
  229. var countOwned = 0;
  230. var eleApps = document.querySelectorAll(query);
  231. for (var i = 0; i < eleApps.length; i++)
  232. {
  233. var appUrl = eleApps[i].getAttribute("href");
  234. if (appUrl.indexOf("http://store.steampowered.com/app/") > -1)
  235. {
  236. var app = /[0-9]+/.exec(appUrl);
  237. if (app != null)
  238. {
  239. if (typeof dataRes[app] !== "undefined")
  240. {
  241. if (dataRes[app].success)
  242. {
  243. if (dataRes[app].data.is_owned)
  244. {
  245. var eleLabel = getLabelCallback(eleApps[i]);
  246. eleLabel.classList.add("bh_owned");
  247. countOwned++;
  248. }
  249. else
  250. {
  251. console.log("App: not owned - http://store.steampowered.com/app/" + app + "/");
  252. }
  253. }
  254. else
  255. {
  256. console.log("App: not success - https://steamdb.info/app/" + app + "/");
  257. }
  258. }
  259. }
  260. }
  261. }
  262. console.log("Apps: owned - " + countOwned);
  263. } // End onload
  264. });
  265. }
  266.  
  267. function main()
  268. {
  269. // #8BC349
  270. // #6EA028
  271. // #2ECC71
  272. // #92B300
  273. GM_addStyle(
  274. " .bh_button { "
  275. + " border-radius: 2px; border: medium none; padding: 10px; display: inline-block; "
  276. + " cursor: pointer; background: #67C1F5 none repeat scroll 0% 0%; "
  277. + " width: 120px; text-align: center; } "
  278. + " .bh_button a { "
  279. + " text-decoration: none !important; color: #FFF !important; "
  280. + " padding: 0px 2px; } "
  281. + " .bh_button:hover a { "
  282. + " color: #0079BF !important; } "
  283. + " .bh_button, .bh_button a { "
  284. + " font-family: Verdana; font-size: 12px; "
  285. + " line-height: 16px; } "
  286. + " .bh_owned { background-color: #7CA156 !important; "
  287. + " transition: background 500ms ease 0s; } "
  288. + " #bh_markOwned { "
  289. + " position: fixed; right: 20px; bottom: 20px; z-index:3; } "
  290. + " #bh_OpenLib { "
  291. + " position: fixed; right: 20px; bottom: 65px; z-index:3; } "
  292. );
  293. var url = document.documentURI;
  294. if (url.indexOf("hrkgame.com") > -1)
  295. {
  296. if (window !== window.parent)
  297. return;
  298. GM_addStyle(
  299. " .bh_owned { background-color: #97BA22 !important; } "
  300. + " #bh_markOwned { bottom: 40px !important; } "
  301. );
  302. if (url.indexOf("/randomkeyshop/make-bundle") > -1)
  303. {
  304. // Add load button
  305. {
  306. var divButton = document.createElement("div");
  307. divButton.classList.add("bh_button");
  308. divButton.id = "bh_loadAll";
  309. divButton.setAttribute("style", "bottom: 86px;");
  310. divButton.setAttribute("onclick", " \
  311. this.firstElementChild.textContent = \"Loading...\"; \
  312. window.scrollTo(0,document.body.scrollHeight); \
  313. var countHidden = 5; \
  314. var idx = setInterval(function(ele) \
  315. { \
  316. var eleLoad = document.querySelector(\"#loader-icon\"); \
  317. if (eleLoad) \
  318. { \
  319. window.scrollTo(0,document.body.scrollHeight); \
  320. if (eleLoad.style.display == \"none\") \
  321. { \
  322. countHidden--; \
  323. } \
  324. else \
  325. { \
  326. countHidden = 5; \
  327. } \
  328. } \
  329. else \
  330. { \
  331. countHidden--; \
  332. } \
  333. if (countHidden < 0) \
  334. { \
  335. clearInterval(idx); \
  336. ele.style.display=\"none\"; \
  337. var eleRes = document.querySelector(\"#result\"); \
  338. if (eleRes) \
  339. { \
  340. eleRes.scrollIntoView(true); \
  341. window.scrollBy(0, -80); \
  342. } \
  343. } \
  344. }, 500, this); \
  345. return false; \
  346. ");
  347. divButton.innerHTML = "<a onclick='return false;'>Load All</a>";
  348. document.body.appendChild(divButton);
  349. }
  350. // Add mark button
  351. {
  352. var divButton = document.createElement("div");
  353. divButton.classList.add("bh_button");
  354. divButton.id = "bh_markOwned";
  355. var eleA = document.createElement("a");
  356. eleA.setAttribute("onclick", "return false;");
  357. eleA.textContent = "Mark Owned";
  358. divButton.appendChild(eleA);
  359. document.body.appendChild(divButton);
  360. divButton.addEventListener("click", function()
  361. {
  362. GM_xmlhttpRequest(
  363. {
  364. method: "GET",
  365. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + randNum(1000, 9999),
  366. onload: function(response)
  367. {
  368. var dataResSteam = JSON.parse(response.responseText);
  369. if (typeof dataResSteam["rgOwnedApps"] == "undefined"
  370. || dataResSteam["rgOwnedApps"].length == 0)
  371. {
  372. console.log("not logged in");
  373. }
  374. else if (typeof dataResSteam["rgOwnedApps"] !== "undefined"
  375. && typeof dataResSteam["rgOwnedPackages"] !== "undefined"
  376. && typeof dataResSteam["rgIgnoredApps"] !== "undefined")
  377. {
  378. var parser = new DOMParser();
  379. var rgxId = /[0-9]+/;
  380. var rgxApp = /:\/\/store\.steampowered\.com\/app\/[0-9]+/i;
  381. var rgxSub = /:\/\/store\.steampowered\.com\/sub\/[0-9]+/i;
  382. var elesProduct = document.querySelectorAll("#result .content:not(.bh_owned)");
  383. var productCur = 0;
  384. var tmId = setInterval(function()
  385. {
  386. if (productCur >= elesProduct.length)
  387. {
  388. clearInterval(tmId);
  389. }
  390. else
  391. {
  392. var dataHref = elesProduct[productCur].firstElementChild.getAttribute("data-href");
  393. if (dataHref != null)
  394. {
  395. var fullHref = "https://www.hrkgame.com" + dataHref + "/";
  396. elesProduct[productCur].setAttribute("bh_href", fullHref);
  397. GM_xmlhttpRequest(
  398. {
  399. method: "GET",
  400. url: fullHref,
  401. onload: function(response)
  402. {
  403. var isOwned = false;
  404. var dataRes = parser.parseFromString(response.responseText, "text/html");
  405. var eleA = dataRes.querySelector(".storeside a.item[href*='store.steampowered.com/']");
  406. if (eleA != null)
  407. {
  408. var attrHref = eleA.href;
  409. var id = rgxId.exec(attrHref);
  410. if (id != null)
  411. {
  412. var valId = parseInt(id);
  413. if (rgxApp.test(attrHref))
  414. {
  415. if (dataResSteam["rgOwnedApps"].indexOf(valId) > -1)
  416. {
  417. isOwned = true;
  418. }
  419. else
  420. {
  421. console.log("App: not owned - http://store.steampowered.com/app/" + id + "/");
  422. }
  423. }
  424. else if (rgxSub.test(attrHref))
  425. {
  426. if (dataResSteam["rgOwnedPackages"].indexOf(valId) > -1)
  427. {
  428. isOwned = true;
  429. }
  430. else
  431. {
  432. console.log("Sub: not owned - http://store.steampowered.com/sub/" + id + "/");
  433. }
  434. }
  435. if (isOwned)
  436. {
  437. for (var i = 0; i < elesProduct.length; i++)
  438. {
  439. if (elesProduct[i].getAttribute("bh_href") == response.finalUrl)
  440. {
  441. elesProduct[i].classList.add("bh_owned");
  442. break;
  443. }
  444. }
  445. }
  446. }
  447. }
  448. else
  449. {
  450. console.log("Info: not found - " + response.finalUrl);
  451. }
  452. } // End onload
  453. });
  454. }
  455. }
  456. productCur++
  457. }, 200);
  458. }
  459. } // End onload
  460. });
  461. });
  462. }
  463. }
  464. else if (url.indexOf("/library") > -1)
  465. {
  466. var clientScript = ' \
  467. confirm = function() \
  468. { \
  469. return true; \
  470. }; \
  471. ';
  472.  
  473. var eleClientScript = document.createElement("script");
  474. eleClientScript.innerHTML = clientScript;
  475. document.head.appendChild(eleClientScript);
  476. }
  477. }
  478. else if (url.indexOf("bundlestars.com") > -1)
  479. {
  480. GM_addStyle(
  481. //" .bh_owned { background-color: #A7CC00 !important; } "
  482. " .bh_owned { background-color: #D0FE00 !important; } "
  483. + " .bh_owned:hover { background-color: #BBE500 !important; } "
  484. + " .bh_owned * { color: #444 !important; } "
  485. + " .bh_owned .was, .bh_owned .was * { color: #777 !important; } "
  486. + " .bh_owned .hide-checkbox + label span { color: #DDD !important; } "
  487. + " .bh_owned .hide-checkbox:checked + label span { color: #D0FE00 !important; } "
  488. + " #launcher { bottom: 100px !important; } "
  489. );
  490. if (url.indexOf("/bundle/") > -1)
  491. {
  492. // Add mark button
  493. {
  494. var divButton = document.createElement("div");
  495. divButton.classList.add("bh_button");
  496. divButton.id = "bh_markOwned";
  497. var eleA = document.createElement("a");
  498. eleA.setAttribute("onclick", "return false;");
  499. eleA.textContent = "Mark Owned";
  500. divButton.appendChild(eleA);
  501. document.body.appendChild(divButton);
  502. divButton.addEventListener("click", function()
  503. {
  504. markOwned(".bundle-accordian a.btn-bundle-more[href*='store.steampowered.com/']", function(ele)
  505. {
  506. ele.parentElement.parentElement
  507. .parentElement.parentElement.parentElement
  508. .parentElement.parentElement.parentElement
  509. .parentElement.firstElementChild
  510. .classList.add("bh_owned");
  511. });
  512. });
  513. }
  514. }
  515. else if (url.indexOf("/promotions/") > -1)
  516. {
  517. // Add mark button
  518. {
  519. var divButton = document.createElement("div");
  520. divButton.classList.add("bh_button");
  521. divButton.id = "bh_markOwned";
  522. var eleA = document.createElement("a");
  523. eleA.setAttribute("onclick", "return false;");
  524. eleA.textContent = "Mark Owned";
  525. divButton.appendChild(eleA);
  526. document.body.appendChild(divButton);
  527. divButton.param_promo = url.substr(url.indexOf("/promotions/") + 12)
  528. .replace(/\?.*/, "").replace(/#.*/, "");
  529. eleA.param_promo = divButton.param_promo;
  530. divButton.addEventListener("click", function(e)
  531. {
  532. var promo = e.target.param_promo;
  533. GM_xmlhttpRequest(
  534. {
  535. method: "GET",
  536. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + randNum(1000, 9999),
  537. onload: function(response)
  538. {
  539. var dataResSteam = JSON.parse(response.responseText);
  540. if (typeof dataResSteam["rgOwnedApps"] == "undefined"
  541. || dataResSteam["rgOwnedApps"].length == 0)
  542. {
  543. console.log("not logged in");
  544. }
  545. else if (typeof dataResSteam["rgOwnedApps"] !== "undefined"
  546. && typeof dataResSteam["rgOwnedPackages"] !== "undefined"
  547. && typeof dataResSteam["rgIgnoredApps"] !== "undefined")
  548. {
  549. var elesProduct = document.querySelectorAll(".bs-card-body:not(.bh_owned)");
  550. for (var i = 0; i < elesProduct.length; i++)
  551. {
  552. var sref = elesProduct[i].firstElementChild.getAttribute("ui-sref");
  553. if (sref != null)
  554. {
  555. elesProduct[i].setAttribute("bh_slug"
  556. , sref.replace("base.game({ slug: '", "")
  557. .replace("base.dlc({ slug: '", "")
  558. .replace("' })", ""));
  559. }
  560. }
  561. GM_xmlhttpRequest(
  562. {
  563. method: "GET",
  564. url: "https://www.bundlestars.com/api/promotions/" + promo,
  565. onload: function(response)
  566. {
  567. var dataRes = JSON.parse(response.responseText);
  568. var slugs = [];
  569. var i = dataRes.length - 1;
  570. //for (var i = 0; i < dataRes.length; i++)
  571. {
  572. for (var j = 0; j < dataRes[i].products.length; j++)
  573. {
  574. if (dataRes[i].products[j].drm.steam)
  575. {
  576. slugs.push(dataRes[i].products[j].slug);
  577. }
  578. }
  579. }
  580. slugs = normalizeArray(slugs);
  581. var slugCur = 0;
  582. var tmId = setInterval(function()
  583. {
  584. if (slugCur >= slugs.length)
  585. {
  586. clearInterval(tmId);
  587. }
  588. else
  589. {
  590. GM_xmlhttpRequest(
  591. {
  592. method: "GET",
  593. url: "https://www.bundlestars.com/api/products/" + slugs[slugCur],
  594. onload: function(response)
  595. {
  596. var isOwned = false;
  597. var dataRes = JSON.parse(response.responseText);
  598. if (!dataRes.steam.sub)
  599. {
  600. if (dataResSteam["rgOwnedApps"].indexOf(dataRes.steam.id) > -1)
  601. {
  602. isOwned = true;
  603. }
  604. else
  605. {
  606. console.log("App: not owned - http://store.steampowered.com/app/" + dataRes.steam.id + "/ - " + dataRes.slug);
  607. }
  608. }
  609. else
  610. {
  611. if (dataResSteam["rgOwnedPackages"].indexOf(dataRes.steam.id) > -1)
  612. {
  613. isOwned = true;
  614. }
  615. else
  616. {
  617. console.log("Sub: not owned - http://store.steampowered.com/sub/" + dataRes.steam.id + "/ - " + dataRes.slug);
  618. }
  619. }
  620. if (isOwned)
  621. {
  622. for (var i = 0; i < elesProduct.length; i++)
  623. {
  624. if (elesProduct[i].getAttribute("bh_slug") == dataRes.slug)
  625. {
  626. elesProduct[i].classList.add("bh_owned");
  627. break;
  628. }
  629. }
  630. }
  631. } // End onload
  632. });
  633. }
  634. slugCur++;
  635. }, 200);
  636. } // End onload
  637. });
  638. }
  639. } // End onload
  640. });
  641. });
  642. }
  643. }
  644. }
  645. else if (url.indexOf("reddit.com") > -1)
  646. {
  647. GM_addStyle(
  648. " .bh_owned , .md .bh_owned code { background-color: #DFF0D8 !important; } "
  649. + " li > .bh_owned { padding: 0px 2px 0px 2px; } "
  650. );
  651. // Add mark button
  652. {
  653. var divButton = document.createElement("div");
  654. divButton.classList.add("bh_button");
  655. divButton.id = "bh_markOwned";
  656. var eleA = document.createElement("a");
  657. eleA.setAttribute("onclick", "return false;");
  658. eleA.textContent = "Mark Owned";
  659. divButton.appendChild(eleA);
  660. //document.body.appendChild(divButton);
  661. divButton.addEventListener("click", function()
  662. {
  663. markOwned("td > a[href*='store.steampowered.com/']", function(ele)
  664. {
  665. ele.parentElement.parentElement.classList.add("bh_owned");
  666. });
  667. });
  668. }
  669. setTimeout(function()
  670. {
  671. markOwned("td > a[href*='store.steampowered.com/']", function(ele)
  672. {
  673. ele.parentElement.parentElement.classList.add("bh_owned");
  674. });
  675. markOwned("li > a[href*='store.steampowered.com/']", function(ele)
  676. {
  677. ele.classList.add("bh_owned");
  678. });
  679. }, 1000);
  680. }
  681. else if (url.indexOf("groupees.com") > -1)
  682. {
  683. GM_addStyle(
  684. " .bh_owned { background-color: #DFF0D8 !important; } "
  685. + " #subscribe-form { display: none; } "
  686. + " input[name='search'] { position: fixed; z-index: 1099; left: 18%; top: 16px; } "
  687. + " button[role='show3dKeyModal'] { position: fixed; z-index: 1099; left: 72%; top: 16px; } "
  688. + " .cancel-spin { position: fixed !important; z-index: 1099 !important; left: 39.5% !important; top: 22px !important; } "
  689. + " .bh_owned_dark { background-color: rgba(140, 197, 63, 0.6) !important; } "
  690. );
  691. /*
  692. var a = document.querySelector("input[name='search']");
  693. var b = document.querySelector("#subscribe-form");
  694. b.parentElement.insertBefore(a,b);
  695. b.parentElement.removeChild(b);
  696. */
  697. // Add mark button
  698. {
  699. var divButton = document.createElement("div");
  700. divButton.classList.add("bh_button");
  701. divButton.id = "bh_markOwned";
  702. var eleA = document.createElement("a");
  703. eleA.setAttribute("onclick", "return false;");
  704. eleA.textContent = "Mark Owned";
  705. divButton.appendChild(eleA);
  706. document.body.appendChild(divButton);
  707. divButton.addEventListener("click", function()
  708. {
  709. var apps = [];
  710. var eleApps = document.querySelectorAll(".bundle > .products .info .description a[href*='store.steampowered.com/app/']"
  711. + ", .expanded .product-info a[href*='store.steampowered.com/app/']"
  712. + ", .product-details.hidden .external-links a[href*='store.steampowered.com/app/']");
  713. console.log("Apps: " + eleApps.length);
  714. for (var i = 0; i < eleApps.length; i++)
  715. {
  716. var app = /[0-9]+/.exec(eleApps[i].getAttribute("href"));
  717. if (app != null)
  718. {
  719. apps.push(app[0]);
  720. }
  721. }
  722. apps = apps.filter(function(elem, index, self)
  723. {
  724. return index == self.indexOf(elem);
  725. });
  726. var appAll = apps.join(",");
  727. GM_xmlhttpRequest(
  728. {
  729. method: "GET",
  730. headers:
  731. {
  732. "Cache-Control": "max-age=0"
  733. },
  734. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  735. onload: function(response)
  736. {
  737. var dataRes = JSON.parse(response.responseText);
  738. var countOwned = 0;
  739. var elePurchases = null;
  740. var elePrds = document.querySelectorAll(".bundle > .products .product h3"
  741. + ", .expanded .details, .product-info");
  742. var eleApps = document.querySelectorAll(".bundle > .products .info"
  743. + ", .expanded .details, .product-details.hidden");
  744. for (var i = 0; i < eleApps.length; i++)
  745. {
  746. var eleApp = eleApps[i].querySelector(".description a[href*='store.steampowered.com/app/']"
  747. + ", .product-info a[href*='store.steampowered.com/app/']"
  748. + ", .external-links a[href*='store.steampowered.com/app/']");
  749. if (eleApp != null)
  750. {
  751. var app = /[0-9]+/.exec(eleApp.getAttribute("href"));
  752. if (app != null)
  753. {
  754. if (typeof dataRes[app] !== "undefined")
  755. {
  756. if (dataRes[app].success)
  757. {
  758. if (dataRes[app].data.is_owned)
  759. {
  760. var eleLabel = elePrds[i];
  761. if (eleLabel.classList.contains("product-info"))
  762. {
  763. eleLabel.classList.add("bh_owned_dark");
  764. // Mark game in build bundles
  765. {
  766. var eleName = eleLabel.querySelector("h4");
  767. if (eleName)
  768. {
  769. var name = eleName.textContent.trim();
  770. if (elePurchases == null)
  771. {
  772. elePurchases = document.querySelectorAll(".purchase-products > .bundle-product > .input > label");
  773. }
  774. if (elePurchases != null)
  775. {
  776. for (var j = 0; j < elePurchases.length; j++)
  777. {
  778. if (elePurchases[j].textContent.trim() == name)
  779. {
  780. elePurchases[j].parentElement.parentElement.classList.add("bh_owned_dark");
  781. }
  782. }
  783. }
  784. }
  785. }
  786. }
  787. else
  788. {
  789. eleLabel.classList.add("bh_owned");
  790. }
  791. countOwned++;
  792. }
  793. else
  794. {
  795. console.log("App: not owned - http://store.steampowered.com/app/" + app);
  796. }
  797. }
  798. else
  799. {
  800. console.log("App: not success - http://store.steampowered.com/app/" + app);
  801. }
  802. }
  803. }
  804. }
  805. }
  806. console.log("Apps: owned - " + countOwned);
  807. } // End onload
  808. });
  809. });
  810. }
  811. }
  812. else if (url.indexOf("indiegala.com") > -1)
  813. {
  814. GM_addStyle(
  815. " .bh_owned { background-color: rgba(125, 174, 45, 0.9) !important; } "
  816. + " .bh_owned .add-info-button-cont .left, .bh_owned .add-info-button-cont .palette-background-2 { "
  817. + " background-color: #7DAE2D !important; } "
  818. + " .bh_owned .add-info-button-cont .right .inner-info, .bh_owned .add-info-button-cont .right .palette-border-2 { "
  819. + " border-color: #7DAE2D !important; } "
  820. + " .bh_owned.medium-game .game-cover-medium { border: 3px solid #7DAE2D; background-color: rgba(125, 174, 45, 0.4); } "
  821. + " .bh_owned.game-data-cont { background-color: #76AD1C !important; } "
  822. + " .bundle-item-trading-cards-cont span { opacity: 0.7; } "
  823. + " .span-title .title_game, .span-title .title_drm, .span-title .title_music { "
  824. + " line-height: 43px !important; margin: 10px 0px 10px 15px !important; "
  825. + " padding-left: 10px !important; border-radius: 3px !important; } "
  826. );
  827. // Auto reload when error
  828. {
  829. setTimeout(function()
  830. {
  831. if (document.title == "500 Server Error")
  832. {
  833. console.log("Autorefresh: 500 Server Error");
  834. setTimeout(function()
  835. {
  836. reload();
  837. }, 5000);
  838. }
  839. }, 10000);
  840. }
  841. // Insert email to bundle section
  842. {
  843. var countRetryEmail = 10;
  844. var tmRetryEmail = setInterval(function()
  845. {
  846. var eleEmail = document.querySelector(".account-email");
  847. var eleInput = document.querySelector(".email-input");
  848. if (eleEmail && eleInput)
  849. {
  850. var email = eleEmail.textContent.trim();
  851. if (email != "")
  852. {
  853. eleInput.value = email;
  854. clearInterval(tmRetryEmail);
  855. }
  856. }
  857. if (countRetryEmail < 0)
  858. {
  859. clearInterval(tmRetryEmail);
  860. }
  861. countRetryEmail--;
  862. }, 3000);
  863. }
  864. // Add mark button
  865. if (url.indexOf("/store/product/") < 0)
  866. {
  867. var divButton = document.createElement("div");
  868. divButton.classList.add("bh_button");
  869. divButton.id = "bh_markOwned";
  870. var eleA = document.createElement("a");
  871. eleA.setAttribute("onclick", "return false;");
  872. eleA.textContent = "Mark Owned";
  873. divButton.appendChild(eleA);
  874. document.body.appendChild(divButton);
  875. divButton.addEventListener("click", function()
  876. {
  877. var rgxId = /[0-9]{3,}/g;
  878. var rgxApp = /:\/\/((store\.steampowered\.com|steamcommunity\.com)\/app|www.indiegala.com\/store\/product\/[^\/]+)\/[0-9]+/i;
  879. var rgxSub = /:\/\/(store\.steampowered\.com|steamcommunity\.com)\/sub\/[0-9]+/i;
  880. var rgxInvalidApp = /:\/\/store\.steampowered\.com\/[0-9]+\//i;
  881. var eleApps = document.querySelectorAll("a[href*='store.steampowered.com/']");
  882. for (var i = 0; i < eleApps.length; i++)
  883. {
  884. var attrHref = eleApps[i].getAttribute("href");
  885. if (rgxInvalidApp.test(attrHref))
  886. {
  887. eleApps[i].setAttribute("href", attrHref.replace("store.steampowered.com/", "store.steampowered.com/app/"));
  888. }
  889. }
  890. GM_xmlhttpRequest(
  891. {
  892. method: "GET",
  893. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + randNum(1000, 9999),
  894. onload: function(response)
  895. {
  896. var dataRes = JSON.parse(response.responseText);
  897. var countOwned = 0;
  898. if (typeof dataRes["rgOwnedApps"] !== "undefined"
  899. && typeof dataRes["rgOwnedPackages"] !== "undefined"
  900. && typeof dataRes["rgIgnoredApps"] !== "undefined")
  901. {
  902. var elePrds = document.querySelectorAll(
  903. ".bundle-item-link, .in .in .in .game-steam-url"
  904. + ", #this_your_gift .game-steam-url");
  905. var eleApps = document.querySelectorAll(
  906. ".game-opened-switcher a[href*='store.steampowered.com/'].game-steam-url"
  907. + ", .game-opened-switcher a[href*='steamcommunity.com/'].game-steam-url"
  908. + ", .in .in .in .game-steam-url"
  909. + ", #this_your_gift .game-steam-url"
  910. + ", .game-cover-medium[href^='/store/product/']"
  911. + ", #game_list_div .game-data-cont a[href^='/store/product/']"
  912. + ", .search-page-store .game-data-cont a[href^='/store/product/']");
  913. var eleTitle = document.querySelector("#title-p");
  914. if (eleTitle && eleTitle.textContent.trim() === "Indiegala gift")
  915. {
  916. elePrds = document.querySelectorAll(
  917. "#steam-key-games .game-steam-url");
  918. eleApps = document.querySelectorAll(
  919. "#steam-key-games .game-steam-url");
  920. }
  921. for (var i = 0; i < eleApps.length; i++)
  922. {
  923. var attrHref = eleApps[i].href;
  924. var ids = attrHref.match(rgxId);
  925. if (ids != null)
  926. {
  927. var valId = parseInt(ids[ids.length - 1]);
  928. var eleLabel = null;
  929. if (eleApps[i].classList.contains("game-cover-medium"))
  930. {
  931. eleLabel = eleApps[i].parentElement;
  932. }
  933. else if (eleApps[i].parentElement.parentElement.classList.contains("game-data-cont"))
  934. {
  935. eleLabel = eleApps[i].parentElement.parentElement;
  936. }
  937. else
  938. {
  939. eleLabel = elePrds[i].parentElement;
  940. }
  941. if (rgxApp.test(attrHref))
  942. {
  943. if (dataRes["rgOwnedApps"].indexOf(valId) > -1)
  944. {
  945. eleLabel.classList.add("bh_owned");
  946. countOwned++;
  947. }
  948. else
  949. {
  950. console.log("App: not owned - http://store.steampowered.com/app/" + valId);
  951. }
  952. }
  953. else if (rgxSub.test(attrHref))
  954. {
  955. if (dataRes["rgOwnedPackages"].indexOf(valId) > -1)
  956. {
  957. eleLabel.classList.add("bh_owned");
  958. countOwned++;
  959. }
  960. else
  961. {
  962. console.log("Sub: not owned - http://store.steampowered.com/sub/" + valId);
  963. }
  964. }
  965. }
  966. }
  967. console.log("Apps: owned - " + countOwned);
  968. }
  969. } // End onload
  970. });
  971. });
  972. }
  973. // Change title
  974. {
  975. var countRetryTitle = 10;
  976. var tmRetryTitle = setInterval(function()
  977. {
  978. var elesPrice = document.querySelectorAll(".bundle-claim-phrase");
  979. for (var i = elesPrice.length - 1; i > -1; i--)
  980. {
  981. var elePrice = elesPrice[i].querySelector("span");
  982. if (elePrice)
  983. {
  984. var price = elePrice.textContent.trim();
  985. if (price.indexOf("$") == 0)
  986. {
  987. document.title = price + " " + document.title;
  988. clearInterval(tmRetryTitle);
  989. break;
  990. }
  991. }
  992. }
  993. if (countRetryTitle < 0)
  994. {
  995. clearInterval(tmRetryTitle);
  996. }
  997. countRetryTitle--;
  998. }, 3000);
  999. }
  1000. // Load library
  1001. if (url.indexOf("/profile") > -1)
  1002. {
  1003. var clientScript = " \
  1004. function openBundleLibrary() \
  1005. { \
  1006. $.ajax({ \
  1007. type: 'GET', \
  1008. data: { user_id : '" + getQueryByName("user_id") + "' }, \
  1009. url: '/ajaxprofile_sale_tab', \
  1010. dataType: 'json', \
  1011. context: $( '#profile_bundle_section .accordion-toggle' ), \
  1012. \
  1013. beforeSend: function(){ \
  1014. console.log('Start: open bundle library' ); \
  1015. openBundleLibraryAjaxSemaphore = false; \
  1016. $('.spinner', $( '#profile_bundle_section .accordion-toggle' ) ).remove(); \
  1017. $( '#profile_bundle_section .accordion-toggle' ).append(\" <span class='spinner'><i class='fa fa-spinner fa-spin'></i></span>\"); \
  1018. }, \
  1019. success: function(data){ \
  1020. console.log('Success: open bundle library' ); \
  1021. $( '#collapseBundles .panel-body' ).html( data['html'] ); \
  1022. setTimeout(function() \
  1023. { \
  1024. $('#profile_bundle_section .accordion-toggle:not([aria-expanded=\"true\"])').click(); \
  1025. }, 500); \
  1026. }, \
  1027. error: function(){ \
  1028. console.log('Error: open bundle library' ); \
  1029. setTimeout(openBundleLibrary, 500); \
  1030. }, \
  1031. complete: function(){ \
  1032. openBundleLibraryAjaxSemaphore = false; \
  1033. $('.spinner', $( '#profile_bundle_section .accordion-toggle' ) ).remove(); \
  1034. }, \
  1035. }); \
  1036. } \
  1037. ";
  1038.  
  1039. var eleClientScript = document.createElement("script");
  1040. eleClientScript.innerHTML = clientScript;
  1041. document.head.appendChild(eleClientScript);
  1042. var divButton = document.createElement("div");
  1043. divButton.classList.add("bh_button");
  1044. divButton.id = "bh_OpenLib";
  1045. divButton.setAttribute("onclick", "openBundleLibrary()");
  1046. var eleA = document.createElement("a");
  1047. eleA.setAttribute("onclick", "return false;");
  1048. eleA.textContent = "Open Library";
  1049. divButton.appendChild(eleA);
  1050. document.body.appendChild(divButton);
  1051. }
  1052. if (url.indexOf("/successpay") > -1)
  1053. {
  1054. setTimeout(function()
  1055. {
  1056. var eleBtn = document.querySelector("#faang.fa-angle-down");
  1057. if (eleBtn)
  1058. {
  1059. eleBtn.click();
  1060. }
  1061. }, 5000);
  1062. }
  1063. }
  1064. else if (url.indexOf("orlygift.com") > -1)
  1065. {
  1066. if (window !== window.parent)
  1067. return;
  1068. // Add mark button
  1069. {
  1070. var divButton = document.createElement("div");
  1071. divButton.classList.add("bh_button");
  1072. divButton.id = "bh_markOwned";
  1073. var eleA = document.createElement("a");
  1074. eleA.textContent = "Mark Owned";
  1075. divButton.appendChild(eleA);
  1076. document.body.appendChild(divButton);
  1077. divButton.addEventListener("click", function()
  1078. {
  1079. var apps = [];
  1080. var eleApps = document.querySelectorAll("div[id^='game-detail-'] a.btn-primary[href^='http://store.steampowered.com/app/']");
  1081. console.log("Apps: " + eleApps.length);
  1082. for (var i = 0; i < eleApps.length; i++)
  1083. {
  1084. var app = /[0-9]+/.exec(eleApps[i].getAttribute("href"));
  1085. if (app != null)
  1086. {
  1087. apps.push(app[0]);
  1088. }
  1089. }
  1090. apps = apps.filter(function(elem, index, self)
  1091. {
  1092. return index == self.indexOf(elem);
  1093. });
  1094. var appAll = apps.join(",");
  1095. GM_xmlhttpRequest(
  1096. {
  1097. method: "GET",
  1098. headers:
  1099. {
  1100. "Cache-Control": "max-age=0"
  1101. },
  1102. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  1103. onload: function(response)
  1104. {
  1105. var dataRes = JSON.parse(response.responseText);
  1106. var countOwned = 0;
  1107. var elePrds = document.querySelectorAll(".box-game");
  1108. var eleApps = document.querySelectorAll("div[id^='game-detail-']");
  1109. for (var i = 0; i < eleApps.length; i++)
  1110. {
  1111. var eleApp = eleApps[i].querySelector("a.btn-primary[href^='http://store.steampowered.com/app/']");
  1112. if (eleApp != null)
  1113. {
  1114. var app = /[0-9]+/.exec(eleApp.getAttribute("href"));
  1115. if (app != null)
  1116. {
  1117. if (typeof dataRes[app] !== "undefined")
  1118. {
  1119. if (dataRes[app].success)
  1120. {
  1121. if (dataRes[app].data.is_owned)
  1122. {
  1123. var eleLabel = elePrds[i];
  1124. eleLabel.classList.add("bh_owned");
  1125. countOwned++;
  1126. }
  1127. else
  1128. {
  1129. console.log("App: not owned - " + app);
  1130. }
  1131. }
  1132. else
  1133. {
  1134. console.log("App: not success - " + app);
  1135. }
  1136. }
  1137. }
  1138. }
  1139. }
  1140. console.log("Apps: owned - " + countOwned);
  1141. } // End onload
  1142. });
  1143. });
  1144. }
  1145. }
  1146. else if (url.indexOf("cubicbundle.com") > -1)
  1147. {
  1148. GM_addStyle(
  1149. " .bh_owned { background-color: #91BA07 !important; } "
  1150. );
  1151. // Add mark button
  1152. {
  1153. var divButton = document.createElement("div");
  1154. divButton.classList.add("bh_button");
  1155. divButton.id = "bh_markOwned";
  1156. var eleA = document.createElement("a");
  1157. eleA.setAttribute("onclick", "return false;");
  1158. eleA.textContent = "Mark Owned";
  1159. divButton.appendChild(eleA);
  1160. document.body.appendChild(divButton);
  1161. divButton.addEventListener("click", function()
  1162. {
  1163. markOwned(".price a[href*='store.steampowered.com/']", function(ele)
  1164. {
  1165. ele.parentElement.parentElement.parentElement.classList.add("bh_owned");
  1166. });
  1167. });
  1168. }
  1169. }
  1170. else if (url.indexOf("dailyindiegame.com") > -1)
  1171. {
  1172. GM_addStyle(
  1173. " .bh_owned, .bh_owned a, .bh_owned a:not(:visited) .DIG2content { color: #202020 !important; } "
  1174. );
  1175. // Add mark button
  1176. {
  1177. var divButton = document.createElement("div");
  1178. divButton.classList.add("bh_button");
  1179. divButton.id = "bh_markOwned";
  1180. var eleA = document.createElement("a");
  1181. eleA.setAttribute("onclick", "return false;");
  1182. eleA.textContent = "Mark Owned";
  1183. divButton.appendChild(eleA);
  1184. document.body.appendChild(divButton);
  1185. divButton.addEventListener("click", function()
  1186. {
  1187. if (document.querySelectorAll(".DIG-content a[href*='store.steampowered.com/']").length > 0)
  1188. {
  1189. markOwned(".DIG-content a[href*='store.steampowered.com/']", function(ele)
  1190. {
  1191. ele.parentElement
  1192. .parentElement.parentElement
  1193. .parentElement.parentElement
  1194. .classList.add("bh_owned");
  1195. });
  1196. }
  1197. else
  1198. {
  1199. markOwned(".DIG2content a[href*='store.steampowered.com/']", function(ele)
  1200. {
  1201. ele.parentElement
  1202. .parentElement
  1203. .classList.add("bh_owned");
  1204. });
  1205. }
  1206. });
  1207. }
  1208. }
  1209. else if (url.indexOf("bundlekings.com") > -1)
  1210. {
  1211. // Add mark button
  1212. {
  1213. var divButton = document.createElement("div");
  1214. divButton.classList.add("bh_button");
  1215. divButton.id = "bh_markOwned";
  1216. var eleA = document.createElement("a");
  1217. eleA.setAttribute("onclick", "return false;");
  1218. eleA.textContent = "Mark Owned";
  1219. divButton.appendChild(eleA);
  1220. document.body.appendChild(divButton);
  1221. divButton.addEventListener("click", function()
  1222. {
  1223. markOwned(".content-wrap a[href*='store.steampowered.com/']", function(ele)
  1224. {
  1225. ele.parentElement.parentElement.parentElement.classList.add("bh_owned");
  1226. });
  1227. });
  1228. }
  1229. }
  1230. else if (url.indexOf("otakumaker.com") > -1)
  1231. {
  1232. GM_addStyle(
  1233. " .bh_owned { background-color: #91BA07 !important; } "
  1234. );
  1235. // Add mark button
  1236. {
  1237. var divButton = document.createElement("div");
  1238. divButton.classList.add("bh_button");
  1239. divButton.id = "bh_markOwned";
  1240. var eleA = document.createElement("a");
  1241. eleA.setAttribute("onclick", "return false;");
  1242. eleA.textContent = "Mark Owned";
  1243. divButton.appendChild(eleA);
  1244. document.body.appendChild(divButton);
  1245. divButton.addEventListener("click", function()
  1246. {
  1247. markOwned(".gantry-width-spacer a[href*='store.steampowered.com/']", function(ele)
  1248. {
  1249. ele.parentElement.parentElement.classList.add("bh_owned");
  1250. });
  1251. });
  1252. }
  1253. }
  1254. else if (url.indexOf("gogobundle.com") > -1)
  1255. {
  1256. GM_addStyle(
  1257. " .bh_owned { background-color: #91BA07 !important; } "
  1258. );
  1259. // Add mark button
  1260. {
  1261. var divButton = document.createElement("div");
  1262. divButton.classList.add("bh_button");
  1263. divButton.id = "bh_markOwned";
  1264. var eleA = document.createElement("a");
  1265. eleA.setAttribute("onclick", "return false;");
  1266. eleA.textContent = "Mark Owned";
  1267. divButton.appendChild(eleA);
  1268. document.body.appendChild(divButton);
  1269. divButton.addEventListener("click", function()
  1270. {
  1271. markOwned(".g-block > .g-block > a[href*='store.steampowered.com/']", function(ele)
  1272. {
  1273. ele.parentElement.parentElement.classList.add("bh_owned");
  1274. });
  1275. });
  1276. }
  1277. }
  1278. else if (url.indexOf("superduperbundle.com") > -1)
  1279. {
  1280. // Add mark button
  1281. {
  1282. var divButton = document.createElement("div");
  1283. divButton.classList.add("bh_button");
  1284. divButton.id = "bh_markOwned";
  1285. var eleA = document.createElement("a");
  1286. eleA.setAttribute("onclick", "return false;");
  1287. eleA.textContent = "Mark Owned";
  1288. divButton.appendChild(eleA);
  1289. document.body.appendChild(divButton);
  1290. divButton.addEventListener("click", function()
  1291. {
  1292. markOwned("#gameslist a[href*='store.steampowered.com/']", function(ele)
  1293. {
  1294. ele.parentElement.parentElement.classList.add("bh_owned");
  1295. });
  1296. });
  1297. }
  1298. }
  1299. else if (url.indexOf("gamebundle.com") > -1)
  1300. {
  1301. GM_addStyle(
  1302. " .bh_owned { background-color: #A0CC41 !important; border-bottom: 45px solid rgba(233, 233, 233, 0.5); } "
  1303. + " .bh_owned .activebundle_game_bundle_debut_title { background-color: #A0CC41 !important; } "
  1304. );
  1305. // Add mark button
  1306. {
  1307. var divButton = document.createElement("div");
  1308. divButton.classList.add("bh_button");
  1309. divButton.id = "bh_markOwned";
  1310. var eleA = document.createElement("a");
  1311. eleA.setAttribute("onclick", "return false;");
  1312. eleA.textContent = "Mark Owned";
  1313. divButton.appendChild(eleA);
  1314. document.body.appendChild(divButton);
  1315. divButton.addEventListener("click", function()
  1316. {
  1317. markOwned(".activebundle_game_section_full a[href*='store.steampowered.com/']", function(ele)
  1318. {
  1319. ele.parentElement.classList.add("bh_owned");
  1320. });
  1321. });
  1322. }
  1323. }
  1324. else if (url.indexOf("humblebundle.com") > -1)
  1325. {
  1326. GM_addStyle(
  1327. " .game-box img { max-height: 180px !important; max-width: 130px !important; } "
  1328. );
  1329. var strBta = "Beat the Average of ";
  1330. var elesPrice = document.querySelectorAll(".hr-payment-tier-heading-text");
  1331. for (var i = 0; i < elesPrice.length; i++)
  1332. {
  1333. var priceRaw = elesPrice[i].textContent.trim();
  1334. if (priceRaw.indexOf(strBta) > -1)
  1335. {
  1336. var price = priceRaw.replace(strBta, "");
  1337. document.title = price + " " + document.title;
  1338. break;
  1339. }
  1340. }
  1341. var eleSold = document.querySelector(".heading-bundles-sold .mini-digit-holder");
  1342. if (eleSold != null)
  1343. {
  1344. var sold = eleSold.getAttribute("data-initial-value") || "";
  1345. eleSold.parentElement.parentElement.setAttribute("title", sold);
  1346. }
  1347. var countRetrySold = 10;
  1348. var tmRetrySold = setInterval(function()
  1349. {
  1350. var sold = "";
  1351. var elesSold = document.querySelectorAll(".mini-digit-holder .top-cutter .heading-num");
  1352. for (var i = 0; i < elesSold.length; i++)
  1353. {
  1354. sold = sold + "" + elesSold[i].textContent.trim();
  1355. }
  1356. if (sold !== "")
  1357. {
  1358. clearInterval(tmRetrySold);
  1359. var eleCount = document.querySelector(".hr-tagline-bundles-sold");
  1360. if (eleCount)
  1361. {
  1362. eleCount.textContent += " (" + sold.replace(/^0+/, "") + ")";
  1363. }
  1364. }
  1365. if (countRetrySold < 0)
  1366. {
  1367. clearInterval(tmRetrySold);
  1368. }
  1369. countRetrySold--;
  1370. }, 1000);
  1371.  
  1372. if (url.indexOf("/downloads") > -1)
  1373. {
  1374. GM_addStyle(
  1375. " #steam-tab .redeem-instructions, #steam-tab .recommend-this-game { display: none; } "
  1376. );
  1377. setTimeout(function()
  1378. {
  1379. var elesKey = document.querySelectorAll(".sr-redeemed-bubble");
  1380. for (var i = 0; i < elesKey.length; i++)
  1381. {
  1382. elesKey[i].addEventListener("click", function (e)
  1383. {
  1384. var ele = e.target;
  1385. clickToSelect(ele);
  1386. });
  1387. }
  1388. }, 3000);
  1389. }
  1390. }
  1391. else if (url.indexOf("steamcompanion.com") > -1)
  1392. {
  1393. GM_addStyle(
  1394. " .bh_owned.banner { margin-bottom: 5px !important; margin-top: 35px !important; "
  1395. + " padding-bottom: 15px !important; padding-top: 15px !important; } "
  1396. + " .bh_owned.giveaway-links { opacity: 0.75; } "
  1397. );
  1398. markOwned("#hero a[href*='store.steampowered.com/']", function(ele)
  1399. {
  1400. ele.classList.add("bh_owned");
  1401. });
  1402. // Mark
  1403. {
  1404. var query = ".giveaway-links img[src^='https://steamcdn-a.akamaihd.net/steam/apps/']";
  1405. var getLabelCallback = function(ele)
  1406. {
  1407. return ele.parentElement.parentElement.parentElement;
  1408. };
  1409. var apps = [];
  1410. var eleApps = document.querySelectorAll(query);
  1411. for (var i = 0; i < eleApps.length; i++)
  1412. {
  1413. var app = /[0-9]+/.exec(eleApps[i].getAttribute("src"));
  1414. if (app != null)
  1415. {
  1416. apps.push(app[0]);
  1417. }
  1418. }
  1419. apps = apps.filter(function(elem, index, self)
  1420. {
  1421. return index == self.indexOf(elem);
  1422. });
  1423. console.log("Apps: " + apps.length);
  1424. var appAll = apps.join(",");
  1425. GM_xmlhttpRequest(
  1426. {
  1427. method: "GET",
  1428. headers:
  1429. {
  1430. "Cache-Control": "max-age=0"
  1431. },
  1432. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  1433. onload: function(response)
  1434. {
  1435. var dataRes = JSON.parse(response.responseText);
  1436. var countOwned = 0;
  1437. var eleApps = document.querySelectorAll(query);
  1438. for (var i = 0; i < eleApps.length; i++)
  1439. {
  1440. var appUrl = eleApps[i].getAttribute("src");
  1441. if (appUrl.indexOf("https://steamcdn-a.akamaihd.net/steam/apps/") > -1)
  1442. {
  1443. var app = /[0-9]+/.exec(appUrl);
  1444. if (app != null)
  1445. {
  1446. if (typeof dataRes[app] !== "undefined")
  1447. {
  1448. if (dataRes[app].success)
  1449. {
  1450. if (dataRes[app].data.is_owned)
  1451. {
  1452. var eleLabel = getLabelCallback(eleApps[i]);
  1453. eleLabel.classList.add("bh_owned");
  1454. countOwned++;
  1455. }
  1456. else
  1457. {
  1458. //console.log("App: not owned - http://store.steampowered.com/app/" + app + "/");
  1459. }
  1460. }
  1461. else
  1462. {
  1463. //console.log("App: not success - https://steamdb.info/app/" + app + "/");
  1464. }
  1465. }
  1466. }
  1467. }
  1468. }
  1469. console.log("Apps: owned - " + countOwned);
  1470. } // End onload
  1471. });
  1472. }
  1473. }
  1474. else if (url.indexOf("store.steampowered.com") > -1)
  1475. {
  1476. if (url.indexOf("/widget/") > -1)
  1477. {
  1478. GM_addStyle(
  1479. " .bh_owned { background-color: transparent !important; } "
  1480. + " .bh_owned a { color: #71A034 !important; }"
  1481. );
  1482. markOwned(".main_text a[href*='store.steampowered.com/']", function(ele)
  1483. {
  1484. ele.parentElement.classList.add("bh_owned");
  1485. });
  1486. }
  1487. else if (url.indexOf("/app/") > -1)
  1488. {
  1489. GM_addStyle(
  1490. " .bh_owned { "
  1491. + " background-color: #6D8C1A !important; "
  1492. + " padding: 0px 2px 0px 2px; "
  1493. + " } "
  1494. );
  1495. markOwned(".glance_details p > a[href*='store.steampowered.com/']"
  1496. + ", .game_area_dlc_bubble a[href*='store.steampowered.com/']",
  1497. function(ele)
  1498. {
  1499. ele.classList.add("bh_owned");
  1500. });
  1501. }
  1502. else if (url.indexOf("/notinterested/") > -1)
  1503. {
  1504. GM_addStyle(
  1505. " .bh_owned { "
  1506. + " background-color: #6D8C1A !important; "
  1507. + " padding: 5px 100px 5px 5px !important; "
  1508. + " margin-left: -5px; margin-right: 50px; "
  1509. + " } "
  1510. );
  1511. markOwned(".ignoredapps > a[href*='store.steampowered.com/']", function(ele)
  1512. {
  1513. ele.classList.add("bh_owned");
  1514. });
  1515. }
  1516. }
  1517. else if (url.indexOf("steamcommunity.com") > -1)
  1518. {
  1519. GM_addStyle(
  1520. " .bh_owned { background-color: #71A034 !important; "
  1521. + " padding: 0px 2px 0px 2px; } "
  1522. + " .bh_owned.blotter_userstatus_game { padding: 0px; border-color: #71A034; } "
  1523. );
  1524. if (url.indexOf("/home") > -1)
  1525. {
  1526. var querySteamHome = ".blotter_gamepurchase_details a[href*='store.steampowered.com/']:not(.bh_owned) "
  1527. + ", .blotter_author_block a[href*='store.steampowered.com/']:not(.bh_owned) "
  1528. + ", .blotter_author_block a[href*='steamcommunity.com/app/']:not(.bh_owned) "
  1529. + ", .blotter_daily_rollup_line a[href*='steamcommunity.com/app/']:not(.bh_owned) ";
  1530. markOwned(querySteamHome, function(ele)
  1531. {
  1532. if (ele.classList.contains("blotter_userstats_game"))
  1533. {
  1534. ele.parentElement.classList.add("bh_owned");
  1535. }
  1536. else
  1537. {
  1538. ele.classList.add("bh_owned");
  1539. }
  1540. });
  1541. var targetObMark = document.getElementById("blotter_content");
  1542. if (targetObMark)
  1543. {
  1544. var tmObMark = -1;
  1545. var obMark = new MutationObserver(function(mutations)
  1546. {
  1547. mutations.forEach(function(mutation)
  1548. {
  1549. clearTimeout(tmObMark);
  1550. tmObMark = setTimeout(function(querySteamHome)
  1551. {
  1552. markOwned(querySteamHome, function(ele)
  1553. {
  1554. if (!ele.classList.contains("blotter_userstats_game"))
  1555. {
  1556. ele.classList.add("bh_owned");
  1557. }
  1558. });
  1559. }, 100, querySteamHome);
  1560. });
  1561. });
  1562.  
  1563. var configObMark = { childList: true };
  1564. obMark.observe(targetObMark, configObMark);
  1565. }
  1566. }
  1567. else if (url.indexOf("/announcements") > -1)
  1568. {
  1569. markOwned(".announcement_body a[href*='store.steampowered.com/']", function(ele)
  1570. {
  1571. ele.classList.add("bh_owned");
  1572. });
  1573. }
  1574. }
  1575. else if (url.indexOf("forums.steampowered.com") > -1)
  1576. {
  1577. GM_addStyle(
  1578. " .bh_owned { background-color: #71A034 !important; "
  1579. + " padding: 0px 2px 0px 2px;"
  1580. + " } "
  1581. );
  1582. markOwned("div[id^='post_message'] a[href*='store.steampowered.com/']", function(ele)
  1583. {
  1584. ele.classList.add("bh_owned");
  1585. });
  1586. }
  1587. else if (url.indexOf("whosgamingnow.net") > -1)
  1588. {
  1589. if (url.indexOf("/discussion") > -1)
  1590. {
  1591. GM_addStyle(
  1592. " .bh_owned { "
  1593. + " padding: 0px 2px 0px 2px;"
  1594. + " } "
  1595. );
  1596. markOwned(".MessageList a[href*='store.steampowered.com/']", function(ele)
  1597. {
  1598. ele.classList.add("bh_owned");
  1599. });
  1600. }
  1601. else if (url.indexOf("/redeem") > -1)
  1602. {
  1603. GM_addStyle(
  1604. " .bh_owned { "
  1605. + " border: 1px solid #FFF;"
  1606. + " } "
  1607. + " .bh_owned .BoxArt { "
  1608. + " border: 0px !important;"
  1609. + " } "
  1610. );
  1611. markOwned(".GameInfo a[href*='store.steampowered.com/']", function(ele)
  1612. {
  1613. ele.parentElement.parentElement.parentElement.classList.add("bh_owned");
  1614. });
  1615. }
  1616. else if (url.indexOf("/giveaway") > -1)
  1617. {
  1618. GM_addStyle(
  1619. " .bh_owned { "
  1620. + " border: 5px solid #7CA156;"
  1621. + " } "
  1622. );
  1623. markOwned("img[src*='://cdn.akamai.steamstatic.com/steam/']", function(ele)
  1624. {
  1625. ele.classList.add("bh_owned");
  1626. });
  1627. }
  1628. }
  1629. else if (url.indexOf("steamground.com") > -1 && url.indexOf("/wholesale") > -1)
  1630. {
  1631. GM_addStyle(
  1632. " .bh_owned { background-color: #48B24B !important; } "
  1633. + " .bh_owned .wholesale-card_title { color: #373d41 !important; } "
  1634. + " .bh_steam { display: none; } "
  1635. );
  1636. var elesTitle = document.querySelectorAll(".wholesale-card_title");
  1637. if (elesTitle.length > 0)
  1638. {
  1639. GM_xmlhttpRequest(
  1640. {
  1641. method: "GET",
  1642. url: "https://www.steamgifts.com/discussion/iy081/steamground-wholesale-build-a-bundle",
  1643. onload: function(response)
  1644. {
  1645. var data = response.responseText;
  1646. var eleContainer = document.createElement("div");
  1647. eleContainer.innerHTML = data;
  1648. var eleComment = eleContainer.querySelector(".comment__description");
  1649. if (eleComment)
  1650. {
  1651. var elesGame = eleComment.querySelectorAll("table td:nth-child(1) a[href*='store.steampowered.com/']");
  1652. if (elesGame.length > 0)
  1653. {
  1654. var arrTitle = [];
  1655. for (var i = 0; i < elesTitle.length; i++)
  1656. {
  1657. arrTitle.push(elesTitle[i].textContent.trim());
  1658. }
  1659. for (var i = 0; i < elesGame.length; i++)
  1660. {
  1661. var isMatch = false;
  1662. var game = elesGame[i].textContent.trim().toLowerCase();
  1663. for (var j = 0; j < elesTitle.length; j++)
  1664. {
  1665. var title = elesTitle[j].textContent.trim().toLowerCase();
  1666. if (game === title
  1667. || (title.indexOf("|") > -1 && game === title.replace("|", ":"))
  1668. || (game === "ball of light" && title === "ball of light (journey)")
  1669. || (game === "its your last chance in new school" && title === "it is yоur last chance in new schооl")
  1670. || (game === "shake your money simulator 2016" && title === "shake your money simulator")
  1671. || (game === "spakoyno: back to the ussr 2.0" && title === "spakoyno back to the ussr 2.0")
  1672. || (game === "or" && title === "or!"))
  1673. {
  1674. isMatch = true;
  1675. arrTitle = arrTitle.filter(function(value)
  1676. {
  1677. return value !== elesTitle[j].textContent.trim()
  1678. });
  1679. }
  1680. if (isMatch)
  1681. {
  1682. var eleA = document.createElement("a");
  1683. eleA.classList.add("bh_steam");
  1684. eleA.href = elesGame[i].href;
  1685. elesTitle[j].parentElement.parentElement.appendChild(eleA);
  1686. break;
  1687. }
  1688. }
  1689. if (!isMatch)
  1690. {
  1691. console.log("Not match: " + elesGame[i].href + " " + elesGame[i].textContent);
  1692. }
  1693. }
  1694.  
  1695. if (arrTitle.length > 0)
  1696. {
  1697. console.log("Not match: " + arrTitle.length);
  1698. for (var i = 0; i < arrTitle.length; i++)
  1699. {
  1700. console.log("Not match: " + arrTitle[i]);
  1701. }
  1702. }
  1703. markOwned(".wholesale-card > a[href*='store.steampowered.com/']", function(ele)
  1704. {
  1705. ele.parentElement.classList.add("bh_owned");
  1706. });
  1707. }
  1708. }
  1709. } // End onload
  1710. });
  1711. }
  1712. }
  1713. else if (url.indexOf("bunchkeys.com") > -1)
  1714. {
  1715. GM_addStyle(
  1716. " .bh_owned { border: #B5D12E 3px solid !important; "
  1717. + " margin-left: -3px; margin-top: -3px; } "
  1718. );
  1719. // Add mark button
  1720. {
  1721. var divButton = document.createElement("div");
  1722. divButton.classList.add("bh_button");
  1723. divButton.id = "bh_markOwned";
  1724. var eleA = document.createElement("a");
  1725. eleA.setAttribute("onclick", "return false;");
  1726. eleA.textContent = "Mark Owned";
  1727. divButton.appendChild(eleA);
  1728. document.body.appendChild(divButton);
  1729. divButton.addEventListener("click", function()
  1730. {
  1731. markOwned("a[href*='store.steampowered.com/']", function(ele)
  1732. {
  1733. ele.parentElement.classList.add("bh_owned");
  1734. });
  1735. });
  1736. }
  1737. }
  1738. }
  1739.  
  1740. attachOnReady(main);
  1741. })();