Bundle Helper

Add tools for many bundle sites.

当前为 2018-08-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Bundle Helper
  3. // @namespace iFantz7E.BundleHelper
  4. // @version 1.07
  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 *://www.gogobundle.com/latest/bundles/*
  10. // @match *://groupees.com/*
  11. // @match *://otakumaker.com/*
  12. // @match *://www.otakumaker.com/*
  13. // @match *://otakubundle.com/latest/bundles/*
  14. // @match *://steamcommunity.com/*/home*
  15. // @match *://steamcommunity.com/groups/*/announcements*
  16. // @match *://steamcompanion.com/gifts/*
  17. // @match *://steamground.com/*
  18. // @match *://store.steampowered.com/account/notinterested/*
  19. // @match *://store.steampowered.com/app/*
  20. // @match *://store.steampowered.com/widget/*
  21. // @match *://store.steampowered.com/search/*
  22. // @match *://whosgamingnow.net/*
  23. // @match *://www.bunchkeys.com/*
  24. // @match *://www.bundlekings.com/*
  25. // @match *://www.fanatical.com/*
  26. // @match *://www.dailyindiegame.com/*
  27. // @match *://www.gamebundle.com/*
  28. // @match *://www.hrkgame.com/*
  29. // @match *://www.humblebundle.com/*
  30. // @match *://www.indiegala.com/*
  31. // @match *://www.orlygift.com/*
  32. // @match *://www.reddit.com/r/*/comments/*
  33. // @match *://www.superduperbundle.com/*
  34. // @match *://www.sgtools.info/*
  35. // @match *://steamkeys.ovh/*
  36. // @match *://steamdb.info/*
  37. // @run-at document-start
  38. // @grant GM_addStyle
  39. // @grant GM_xmlhttpRequest
  40. // @grant GM_getValue
  41. // @grant GM_setValue
  42. // @connect store.steampowered.com
  43. // @connect www.hrkgame.com
  44. // @connect www.fanatical.com
  45. // @connect www.steamgifts.com
  46. // @icon https://store.steampowered.com/favicon.ico
  47. // @license GPL-3.0-only
  48. // @copyright 2016, 7-elephant
  49. // @supportURL https://steamcommunity.com/id/7-elephant/
  50. // @contributionURL https://www.paypal.me/iFantz7E
  51. // ==/UserScript==
  52.  
  53. // Connect to store.steampowered.com to get owner info
  54. // Connect to www.hrkgame.com and www.fanatical.com to get Steam ID of each products
  55. // Connect to www.steamgifts.com to get bundle threads
  56.  
  57. // License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
  58.  
  59. // Since 10 Jan 2016
  60. // https://greasyfork.org/scripts/16105-bundle-helper/
  61.  
  62. (function ()
  63. {
  64. "use strict";
  65. // jshint multistr:true
  66.  
  67. function attachOnLoad(callback)
  68. {
  69. window.addEventListener("load", function (e)
  70. {
  71. callback();
  72. });
  73. }
  74.  
  75. function attachOnReady(callback)
  76. {
  77. document.addEventListener("DOMContentLoaded", function (e)
  78. {
  79. callback();
  80. });
  81. }
  82.  
  83. function insertBeforeElement(newNode, referenceNode)
  84. {
  85. referenceNode.parentNode.insertBefore(newNode, referenceNode);
  86. }
  87.  
  88. function insertAfterElement(newNode, referenceNode)
  89. {
  90. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  91. }
  92.  
  93. function reload()
  94. {
  95. var curHref = window.location.href;
  96. var posHashtag = curHref.indexOf("#");
  97. if (posHashtag > -1)
  98. {
  99. window.location = curHref.substr(0, posHashtag);
  100. }
  101. else
  102. {
  103. window.location = curHref;
  104. }
  105. }
  106.  
  107. function getQueryByName(name, url)
  108. {
  109. if (url == null)
  110. {
  111. url = location == null ? "" : location.search;
  112. }
  113. name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  114. var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
  115. var results = regex.exec(url);
  116. var retVal = "";
  117. if (results != null)
  118. {
  119. retVal = results[1].replace(/\+/g, " ");
  120. try
  121. {
  122. retVal = decodeURIComponent(retVal);
  123. }
  124. catch (ex)
  125. {
  126. console.error("getQueryByName", ex.message);
  127. }
  128. }
  129. return retVal;
  130. }
  131.  
  132. function clickToSelect(ele)
  133. {
  134. if (ele != null)
  135. {
  136. var range = document.createRange();
  137. range.setStartBefore(ele.firstChild);
  138. range.setEndAfter(ele.lastChild);
  139.  
  140. var sel = window.getSelection();
  141. sel.removeAllRanges();
  142. sel.addRange(range);
  143. }
  144. }
  145.  
  146. function scrollToElement(selector, offset)
  147. {
  148. if (typeof offset === "undefined")
  149. {
  150. offset = -20;
  151. }
  152.  
  153. var ele = null;
  154. if (selector)
  155. {
  156. if (selector instanceof HTMLElement)
  157. {
  158. ele = selector;
  159. }
  160. else
  161. {
  162. ele = document.querySelector(selector);
  163. }
  164. if (ele)
  165. {
  166. ele.scrollIntoView(true);
  167. window.scrollBy(0, offset);
  168. }
  169. }
  170. }
  171.  
  172. function normalizeArray(arr)
  173. {
  174. arr = arr.filter(function(elem, index, self)
  175. {
  176. return index == self.indexOf(elem);
  177. });
  178. return arr;
  179. }
  180.  
  181. function randNum(min, max)
  182. {
  183. return Math.round(Math.random() * (max - min) + min);
  184. }
  185.  
  186. var timeoutList = [];
  187. var intervalList = [];
  188.  
  189. function setTimeoutCustom(func, tm, params)
  190. {
  191. var id = setTimeout(func, tm, params);
  192. timeoutList.push(id);
  193. return id;
  194. }
  195.  
  196. function clearTimeoutAll()
  197. {
  198. for (var i = 0; i < timeoutList.length; i++)
  199. {
  200. clearTimeout(timeoutList[i]);
  201. }
  202. }
  203.  
  204. function setIntervalCustom(func, tm, params)
  205. {
  206. var id = setInterval(func, tm, params);
  207. intervalList.push(id);
  208. return id;
  209. }
  210.  
  211. function clearIntervalAll()
  212. {
  213. for (var i = 0; i < intervalList.length; i++)
  214. {
  215. clearInterval(intervalList[i]);
  216. }
  217. }
  218.  
  219. function getUnixTimestamp()
  220. {
  221. return parseInt(Date.now() / 1000);
  222. }
  223.  
  224. function resetProfileCacheTimestamp()
  225. {
  226. GM_setValue(name_profile_time, 0);
  227. console.log("Cache: refresh");
  228. }
  229.  
  230. function isProfileCacheExpired()
  231. {
  232. var isExpired = false;
  233. var timestampExpired = 5 * 60;
  234.  
  235. var profileTimestamp = GM_getValue(name_profile_time, 0);
  236.  
  237. var profileTimestampDiff = getUnixTimestamp() - profileTimestamp;
  238. if (profileTimestampDiff > timestampExpired)
  239. {
  240. isExpired = true;
  241. }
  242.  
  243. if (!isExpired)
  244. {
  245. var profileJson = GM_getValue(name_profile_json, 0);
  246. if (!profileJson)
  247. {
  248. isExpired = true;
  249. }
  250. }
  251.  
  252. if (!isExpired)
  253. {
  254. console.log("Cache: " + profileTimestampDiff + "s");
  255. }
  256.  
  257. return isExpired;
  258. }
  259.  
  260. function setProfileCache(json)
  261. {
  262. GM_setValue(name_profile_json, json);
  263. GM_setValue(name_profile_time, getUnixTimestamp());
  264. }
  265.  
  266. function markOwned(query, getElementCallback, getProductIdCallback
  267. , classOwned, classNotInterested, classWished, getCountCallback)
  268. {
  269. if (!document.querySelector(query))
  270. {
  271. //console.log("markOwned: empty");
  272. return;
  273. }
  274.  
  275. if (!getElementCallback)
  276. {
  277. getElementCallback = function(ele, type)
  278. {
  279. // type -> 1: Owned, 2: Ignored, 3: Wishlist
  280. return ele;
  281. };
  282. }
  283.  
  284. if (!getProductIdCallback)
  285. {
  286. getProductIdCallback = function(ele)
  287. {
  288. return ele.getAttribute("href");
  289. };
  290. }
  291.  
  292. if (!getCountCallback)
  293. {
  294. getCountCallback = function(appCount, subCount, appOwned, subOwned)
  295. {
  296. };
  297. }
  298.  
  299. if (!classOwned) classOwned = "";
  300. if (!classNotInterested) classNotInterested = "";
  301. if (!classWished) classWished = "";
  302.  
  303. var apps = [];
  304. var subs = [];
  305.  
  306. var rgxId = /[0-9]{3,}/g;
  307. var rgxApp = /((:\/\/(store\.steampowered\.com|steamcommunity\.com|steamdb\.info)\/app|\/steam\/apps)\/[0-9]+|^[0-9]{3,}$)/i;
  308. var rgxSub = /(:\/\/(store\.steampowered\.com|steamdb\.info)\/sub|\/steam\/subs)\/[0-9]+/i;
  309.  
  310. var markFromJson = function(dataRes)
  311. {
  312. if (!dataRes)
  313. {
  314. //console.log("markFromJson: empty");
  315. return;
  316. }
  317.  
  318. var countOwned = [0, 0];
  319. var countAll = [0, 0];
  320.  
  321. if (typeof dataRes["rgOwnedApps"] !== "undefined"
  322. && typeof dataRes["rgOwnedPackages"] !== "undefined"
  323. && typeof dataRes["rgIgnoredApps"] !== "undefined")
  324. {
  325. var eleApps = document.querySelectorAll(query);
  326. for (var i = 0; i < eleApps.length; i++)
  327. {
  328. var attrHref = getProductIdCallback(eleApps[i]);
  329. var ids = attrHref.match(rgxId);
  330. if (ids)
  331. {
  332. var valId = parseInt(ids[0]);
  333.  
  334. if (rgxApp.test(attrHref))
  335. {
  336. if (dataRes["rgOwnedApps"].indexOf(valId) > -1)
  337. {
  338. var ele = getElementCallback(eleApps[i], 1);
  339. if (ele && classOwned !== "")
  340. {
  341. ele.classList.add(classOwned);
  342. }
  343. countOwned[0]++;
  344. }
  345. else
  346. {
  347. //console.log("App: not owned - http://store.steampowered.com/app/" + id + "/");
  348. if (dataRes["rgWishlist"].indexOf(valId) > -1)
  349. {
  350. var ele = getElementCallback(eleApps[i], 3);
  351. if (ele && classWished !== "")
  352. {
  353. ele.classList.add(classWished);
  354. }
  355. }
  356. else if (dataRes["rgIgnoredApps"].indexOf(valId) > -1)
  357. {
  358. var ele = getElementCallback(eleApps[i], 2);
  359. if (ele && classNotInterested !== "")
  360. {
  361. ele.classList.add(classNotInterested);
  362. }
  363. }
  364. }
  365. countAll[0]++;
  366. }
  367. else if (rgxSub.test(attrHref))
  368. {
  369. if (dataRes["rgOwnedPackages"].indexOf(valId) > -1)
  370. {
  371. var ele = getElementCallback(eleApps[i], 1);
  372. if (ele && classOwned !== "")
  373. {
  374. ele.classList.add(classOwned);
  375. }
  376. countOwned[1]++;
  377. }
  378. else
  379. {
  380. //console.log("Sub: not owned - http://store.steampowered.com/sub/" + id + "/");
  381. }
  382. countAll[1]++;
  383. }
  384. }
  385. }
  386. }
  387.  
  388. if (countAll[0] > 1)
  389. {
  390. console.log("App: owned " + countOwned[0] + "/" + countAll[0]);
  391. }
  392. if (countAll[1] > 1)
  393. {
  394. console.log("Sub: owned " + countOwned[1] + "/" + countAll[1]);
  395. }
  396.  
  397. getCountCallback(countAll[0], countAll[1], countOwned[0], countOwned[1]);
  398. }
  399.  
  400. if (isProfileCacheExpired())
  401. {
  402. GM_xmlhttpRequest(
  403. {
  404. method: "GET",
  405. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + getUnixTimestamp(),
  406. onload: function(response)
  407. {
  408. console.log("markOwned: userdata " + response.responseText.length + " bytes");
  409.  
  410. var dataRes = JSON.parse(response.responseText);
  411.  
  412. setProfileCache(dataRes);
  413. markFromJson(dataRes);
  414.  
  415. } // End onload
  416. });
  417. }
  418. else
  419. {
  420. setTimeoutCustom(function()
  421. {
  422. // Delay after script ran
  423. var profileJson = GM_getValue(name_profile_json, 0);
  424. markFromJson(profileJson);
  425. }, 300);
  426. }
  427. }
  428.  
  429. function markOwned_old(query, markOwnedCallback)
  430. {
  431. var rgxId = /[0-9]{3,}/g;
  432. var rgxApp = /:\/\/((store\.steampowered|steamcommunity)\.com\/app|cdn.akamai.steamstatic.com\/steam\/apps)\/[0-9]+/i;
  433. var rgxSub = /:\/\/store\.steampowered\.com\/sub\/[0-9]+/i;
  434.  
  435. GM_xmlhttpRequest(
  436. {
  437. method: "GET",
  438. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + getUnixTimestamp(),
  439. onload: function(response)
  440. {
  441. var dataRes = JSON.parse(response.responseText);
  442.  
  443. var countOwned = [0, 0];
  444. var countAll = [0, 0];
  445.  
  446. if (typeof dataRes["rgOwnedApps"] !== "undefined"
  447. && typeof dataRes["rgOwnedPackages"] !== "undefined"
  448. && typeof dataRes["rgIgnoredApps"] !== "undefined")
  449. {
  450. var eleApps = document.querySelectorAll(query);
  451. for (var i = 0; i < eleApps.length; i++)
  452. {
  453. var attrHref = eleApps[i].getAttribute("href") || eleApps[i].getAttribute("src");
  454. var ids = attrHref.match(rgxId);
  455. if (ids != null)
  456. {
  457. var valId = parseInt(ids[0]);
  458.  
  459. if (rgxApp.test(attrHref))
  460. {
  461. if (dataRes["rgOwnedApps"].indexOf(valId) > -1)
  462. {
  463. markOwnedCallback(eleApps[i]);
  464. countOwned[0]++;
  465. }
  466. else
  467. {
  468. console.log("App: not owned - http://store.steampowered.com/app/" + valId + "/");
  469. }
  470. countAll[0]++;
  471. }
  472. else if (rgxSub.test(attrHref))
  473. {
  474. if (dataRes["rgOwnedPackages"].indexOf(valId) > -1)
  475. {
  476. markOwnedCallback(eleApps[i]);
  477. countOwned[1]++;
  478. }
  479. else
  480. {
  481. console.log("Sub: not owned - http://store.steampowered.com/sub/" + valId + "/");
  482. }
  483. countAll[1]++;
  484. }
  485. }
  486. }
  487.  
  488. }
  489.  
  490. var diff = countAll[0] - countOwned[0];
  491. console.log("App: " + countOwned[0] + "/" + countAll[0] + (diff > 10 ? " Diff: " + diff : ""));
  492. console.log("Sub: " + countOwned[1] + "/" + countAll[1]);
  493.  
  494. } // End onload
  495. });
  496. }
  497.  
  498. var name_profile_json = "bh_profile_json";
  499. var name_profile_time = "bh_profile_time";
  500.  
  501. function main()
  502. {
  503. // #8BC349
  504. // #6EA028
  505. // #2ECC71
  506. // #92B300
  507.  
  508. GM_addStyle(
  509. " .bh_button { "
  510. + " border-radius: 2px; border: medium none; padding: 10px; display: inline-block; "
  511. + " cursor: pointer; background: #67C1F5 none repeat scroll 0% 0%; "
  512. + " width: 120px; text-align: center; } "
  513. + " .bh_button a { "
  514. + " text-decoration: none !important; color: #FFF !important; "
  515. + " padding: 0px 2px; } "
  516. + " .bh_button:hover a { "
  517. + " color: #0079BF !important; } "
  518. + " .bh_button, .bh_button a { "
  519. + " font-family: Verdana; font-size: 12px; "
  520. + " line-height: 16px; } "
  521. + " .bh_owned { background-color: #7CA156 !important; "
  522. + " transition: background 500ms ease 0s; } "
  523. + " #bh_markOwned { "
  524. + " position: fixed; right: 20px; bottom: 20px; z-index: 33; } "
  525. + " #bh_OpenLib { "
  526. + " position: fixed; right: 20px; bottom: 65px; z-index: 33; } "
  527. );
  528.  
  529. var url = document.documentURI;
  530.  
  531. if (url.indexOf("hrkgame.com") > -1)
  532. {
  533. if (window !== window.parent)
  534. return;
  535.  
  536. GM_addStyle(
  537. " .bh_owned { background-color: #97BA22 !important; } "
  538. + " #bh_markOwned { bottom: 40px !important; } "
  539. );
  540.  
  541. if (url.indexOf("/randomkeyshop/make-bundle") > -1)
  542. {
  543. // Add load button
  544. {
  545. var divButton = document.createElement("div");
  546. divButton.classList.add("bh_button");
  547. divButton.id = "bh_loadAll";
  548. divButton.setAttribute("style", "bottom: 86px;");
  549. divButton.setAttribute("onclick", " \
  550. this.firstElementChild.textContent = \"Loading...\"; \
  551. window.scrollTo(0,document.body.scrollHeight); \
  552. var countHidden = 5; \
  553. var idx = setInterval(function(ele) \
  554. { \
  555. var eleLoad = document.querySelector(\"#loader-icon\"); \
  556. if (eleLoad) \
  557. { \
  558. window.scrollTo(0,document.body.scrollHeight); \
  559. if (eleLoad.style.display == \"none\") \
  560. { \
  561. countHidden--; \
  562. } \
  563. else \
  564. { \
  565. countHidden = 5; \
  566. } \
  567. } \
  568. else \
  569. { \
  570. countHidden--; \
  571. } \
  572. if (countHidden < 0) \
  573. { \
  574. clearInterval(idx); \
  575. ele.style.display=\"none\"; \
  576. var eleRes = document.querySelector(\"#result\"); \
  577. if (eleRes) \
  578. { \
  579. eleRes.scrollIntoView(true); \
  580. window.scrollBy(0, -80); \
  581. } \
  582. } \
  583. }, 500, this); \
  584. return false; \
  585. ");
  586. divButton.innerHTML = "<a onclick='return false;'>Load All</a>";
  587. document.body.appendChild(divButton);
  588. }
  589.  
  590. // Add mark button
  591. {
  592. var divButton = document.createElement("div");
  593. divButton.classList.add("bh_button");
  594. divButton.id = "bh_markOwned";
  595.  
  596. var eleA = document.createElement("a");
  597. eleA.setAttribute("onclick", "return false;");
  598. eleA.textContent = "Mark Owned";
  599.  
  600. divButton.appendChild(eleA);
  601. document.body.appendChild(divButton);
  602.  
  603. divButton.addEventListener("click", function()
  604. {
  605. GM_xmlhttpRequest(
  606. {
  607. method: "GET",
  608. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + getUnixTimestamp(),
  609. onload: function(response)
  610. {
  611. var dataResSteam = JSON.parse(response.responseText);
  612.  
  613. if (typeof dataResSteam["rgOwnedApps"] == "undefined"
  614. || dataResSteam["rgOwnedApps"].length == 0)
  615. {
  616. console.log("not logged in");
  617. }
  618. else if (typeof dataResSteam["rgOwnedApps"] !== "undefined"
  619. && typeof dataResSteam["rgOwnedPackages"] !== "undefined"
  620. && typeof dataResSteam["rgIgnoredApps"] !== "undefined")
  621. {
  622. var parser = new DOMParser();
  623. var rgxId = /[0-9]+/;
  624. var rgxApp = /:\/\/store\.steampowered\.com\/app\/[0-9]+/i;
  625. var rgxSub = /:\/\/store\.steampowered\.com\/sub\/[0-9]+/i;
  626.  
  627. var elesProduct = document.querySelectorAll("#result .content:not(.bh_owned)");
  628.  
  629. var productCur = 0;
  630. var tmId = setInterval(function()
  631. {
  632. if (productCur >= elesProduct.length)
  633. {
  634. clearInterval(tmId);
  635. }
  636. else
  637. {
  638. var dataHref = elesProduct[productCur].firstElementChild.getAttribute("data-href");
  639. if (dataHref != null)
  640. {
  641. var fullHref = "https://www.hrkgame.com" + dataHref + "/";
  642. elesProduct[productCur].setAttribute("bh_href", fullHref);
  643.  
  644. GM_xmlhttpRequest(
  645. {
  646. method: "GET",
  647. url: fullHref,
  648. onload: function(response)
  649. {
  650. var isOwned = false;
  651.  
  652. var dataRes = parser.parseFromString(response.responseText, "text/html");
  653.  
  654. var eleA = dataRes.querySelector(".storeside a.item[href*='store.steampowered.com/']");
  655. if (eleA != null)
  656. {
  657. var attrHref = eleA.href;
  658. var id = rgxId.exec(attrHref);
  659. if (id != null)
  660. {
  661. var valId = parseInt(id);
  662.  
  663. if (rgxApp.test(attrHref))
  664. {
  665. if (dataResSteam["rgOwnedApps"].indexOf(valId) > -1)
  666. {
  667. isOwned = true;
  668. }
  669. else
  670. {
  671. console.log("App: not owned - http://store.steampowered.com/app/" + id + "/");
  672. }
  673. }
  674. else if (rgxSub.test(attrHref))
  675. {
  676. if (dataResSteam["rgOwnedPackages"].indexOf(valId) > -1)
  677. {
  678. isOwned = true;
  679. }
  680. else
  681. {
  682. console.log("Sub: not owned - http://store.steampowered.com/sub/" + id + "/");
  683. }
  684. }
  685.  
  686. if (isOwned)
  687. {
  688. for (var i = 0; i < elesProduct.length; i++)
  689. {
  690. if (elesProduct[i].getAttribute("bh_href") == response.finalUrl)
  691. {
  692. elesProduct[i].classList.add("bh_owned");
  693. break;
  694. }
  695. }
  696. }
  697. }
  698. }
  699. else
  700. {
  701. console.log("Info: not found - " + response.finalUrl);
  702. }
  703.  
  704. } // End onload
  705. });
  706. }
  707. }
  708. productCur++
  709. }, 200);
  710. }
  711.  
  712. } // End onload
  713. });
  714. });
  715. }
  716. }
  717. else if (url.indexOf("/library") > -1)
  718. {
  719. var clientScript = ' \
  720. confirm = function() \
  721. { \
  722. return true; \
  723. }; \
  724. ';
  725.  
  726. var eleClientScript = document.createElement("script");
  727. eleClientScript.innerHTML = clientScript;
  728. document.head.appendChild(eleClientScript);
  729. }
  730. }
  731. else if (url.indexOf("fanatical.com") > -1)
  732. {
  733. GM_addStyle(
  734. //" .bh_owned { background-color: #A7CC00 !important; } "
  735. " .bh_owned { background-color: #D0FE00 !important; } "
  736. + " .bh_owned:hover { background-color: #BBE500 !important; } "
  737. + " .bh_owned *, .bh_owned.pnm-product-card .card-block .card-title { "
  738. + " color: #444 !important; fill: #444 !important; } "
  739. + " .bh_owned .was, .bh_owned .was * { color: #777 !important; } "
  740. + " .bh_owned .hide-checkbox + label span { color: #DDD !important; } "
  741. + " .bh_owned .hide-checkbox:checked + label span { color: #D0FE00 !important; } "
  742. + " .bh_owned .text-fade { display: none !important; } "
  743. + " #launcher { bottom: 100px !important; } "
  744. );
  745.  
  746. if (url.indexOf("/bundle/") > -1)
  747. {
  748. var fn_markOwned = function()
  749. {
  750. markOwned(".product-details a[href*='store.steampowered.com/']", function(ele)
  751. {
  752. return ele.parentElement.parentElement
  753. .parentElement.parentElement.parentElement
  754. .parentElement.parentElement.parentElement
  755. .parentElement.parentElement.parentElement
  756. .parentElement.firstElementChild.firstElementChild;
  757. }, null, "bh_owned");
  758. };
  759.  
  760. var obTarget_root = document.querySelector("#root");
  761. if (obTarget_root)
  762. {
  763. var tmOb_root = -1;
  764. var obMu_root = new MutationObserver(function(mutations)
  765. {
  766. mutations.forEach(function(mutation)
  767. {
  768. if (mutation.type !== "attributes"
  769. || mutation.target.tagName === "TR")
  770. {
  771. clearTimeout(tmOb_root);
  772. tmOb_root = setTimeoutCustom(function()
  773. {
  774. fn_markOwned();
  775. }, 200);
  776. }
  777. });
  778. });
  779.  
  780. var obConfig_root = { childList: true, subtree: true };
  781. obMu_root.observe(obTarget_root, obConfig_root);
  782. }
  783.  
  784. // Add mark button
  785. /*{
  786. var divButton = document.createElement("div");
  787. divButton.classList.add("bh_button");
  788. divButton.id = "bh_markOwned";
  789.  
  790. var eleA = document.createElement("a");
  791. eleA.setAttribute("onclick", "return false;");
  792. eleA.textContent = "Mark Owned";
  793.  
  794. divButton.appendChild(eleA);
  795. document.body.appendChild(divButton);
  796.  
  797. divButton.addEventListener("click", function()
  798. {
  799. fn_markOwned();
  800. });
  801. }*/
  802. }
  803. else if (url.indexOf("/pick-and-mix/") > -1)
  804. {
  805. // Add mark button
  806. {
  807. var divButton = document.createElement("div");
  808. divButton.classList.add("bh_button");
  809. divButton.id = "bh_markOwned";
  810.  
  811. var eleA = document.createElement("a");
  812. eleA.setAttribute("onclick", "return false;");
  813. eleA.textContent = "Mark Owned";
  814.  
  815. divButton.appendChild(eleA);
  816. document.body.appendChild(divButton);
  817.  
  818. divButton.param_promo = url.substr(url.indexOf("/pick-and-mix/") + 14)
  819. .replace(/\?.+/, "").replace(/#.+/, "");
  820. eleA.param_promo = divButton.param_promo;
  821.  
  822. divButton.addEventListener("click", function(e)
  823. {
  824. var promo = e.target.param_promo;
  825.  
  826. GM_xmlhttpRequest(
  827. {
  828. method: "GET",
  829. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + getUnixTimestamp(),
  830. onload: function(response)
  831. {
  832. var dataResSteam = JSON.parse(response.responseText);
  833.  
  834. if (typeof dataResSteam["rgOwnedApps"] == "undefined"
  835. || dataResSteam["rgOwnedApps"].length == 0)
  836. {
  837. console.log("not logged in");
  838. }
  839. else if (typeof dataResSteam["rgOwnedApps"] !== "undefined"
  840. && typeof dataResSteam["rgOwnedPackages"] !== "undefined"
  841. && typeof dataResSteam["rgIgnoredApps"] !== "undefined")
  842. {
  843. var elesProduct = document.querySelectorAll(".pnm-product-card:not(.bh_owned)");
  844. for (var i = 0; i < elesProduct.length; i++)
  845. {
  846. var eleSlug = elesProduct[i].querySelector(".faux-block-link__overlay-link");
  847. if (eleSlug)
  848. {
  849. var href = eleSlug.href;
  850. elesProduct[i].dataset.slug = href.substr(href.indexOf("/game/") + 6);
  851. }
  852. }
  853.  
  854. GM_xmlhttpRequest(
  855. {
  856. method: "GET",
  857. url: "https://api.fanatical.com/api/promotions/" + promo,
  858. onload: function(response)
  859. {
  860. var dataRes = JSON.parse(response.responseText);
  861.  
  862. var slugs = [];
  863.  
  864. var i = dataRes.length - 1;
  865. //for (var i = 0; i < dataRes.length; i++)
  866. {
  867.  
  868. for (var j = 0; j < dataRes[i].products.length; j++)
  869. {
  870. if (dataRes[i].products[j].drm.steam)
  871. {
  872. slugs.push(dataRes[i].products[j].slug);
  873. }
  874. }
  875. }
  876.  
  877. slugs = normalizeArray(slugs);
  878.  
  879. var slugCur = 0;
  880. var tmId = setInterval(function()
  881. {
  882. if (slugCur >= slugs.length)
  883. {
  884. clearInterval(tmId);
  885. }
  886. else
  887. {
  888. GM_xmlhttpRequest(
  889. {
  890. method: "GET",
  891. url: "https://api.fanatical.com/api/products/" + slugs[slugCur],
  892. onload: function(response)
  893. {
  894. var isOwned = false;
  895.  
  896. var dataRes = JSON.parse(response.responseText);
  897.  
  898. if (!dataRes.steam.sub)
  899. {
  900. if (dataResSteam["rgOwnedApps"].indexOf(dataRes.steam.id) > -1)
  901. {
  902. isOwned = true;
  903. }
  904. else
  905. {
  906. console.log("App: not owned - http://store.steampowered.com/app/" + dataRes.steam.id + "/ - " + dataRes.slug);
  907. }
  908. }
  909. else
  910. {
  911. if (dataResSteam["rgOwnedPackages"].indexOf(dataRes.steam.id) > -1)
  912. {
  913. isOwned = true;
  914. }
  915. else
  916. {
  917. console.log("Sub: not owned - http://store.steampowered.com/sub/" + dataRes.steam.id + "/ - " + dataRes.slug);
  918. }
  919. }
  920.  
  921. if (isOwned)
  922. {
  923. for (var i = 0; i < elesProduct.length; i++)
  924. {
  925. if (elesProduct[i].dataset.slug === dataRes.slug)
  926. {
  927. elesProduct[i].classList.add("bh_owned");
  928. break;
  929. }
  930. }
  931. }
  932.  
  933. } // End onload
  934. });
  935. }
  936. slugCur++;
  937. }, 200);
  938.  
  939. } // End onload
  940. });
  941.  
  942. }
  943.  
  944. } // End onload
  945. });
  946. });
  947. }
  948. }
  949. }
  950. else if (url.indexOf("reddit.com") > -1)
  951. {
  952. GM_addStyle(
  953. " .bh_owned , .md .bh_owned code { background-color: #DFF0D8 !important; } "
  954. + " li > .bh_owned, div > p > .bh_owned { padding: 0px 2px 0px 2px; } "
  955. );
  956.  
  957. // Add mark button
  958. {
  959. var divButton = document.createElement("div");
  960. divButton.classList.add("bh_button");
  961. divButton.id = "bh_markOwned";
  962.  
  963. var eleA = document.createElement("a");
  964. eleA.setAttribute("onclick", "return false;");
  965. eleA.textContent = "Mark Owned";
  966.  
  967. divButton.appendChild(eleA);
  968. //document.body.appendChild(divButton);
  969.  
  970. divButton.addEventListener("click", function()
  971. {
  972. markOwned("td > a[href*='store.steampowered.com/']", function(ele)
  973. {
  974. return ele.parentElement.parentElement;
  975. }, null, "bh_owned");
  976. });
  977. }
  978.  
  979. setTimeout(function()
  980. {
  981. markOwned("td > a[href*='store.steampowered.com/']", function(ele)
  982. {
  983. return ele.parentElement.parentElement;
  984. }, null, "bh_owned");
  985.  
  986. markOwned("li > a[href*='store.steampowered.com/']", function(ele)
  987. {
  988. return ele.parentElement;
  989. }, null, "bh_owned");
  990.  
  991. markOwned("li > p > a[href*='store.steampowered.com/']", function(ele)
  992. {
  993. return ele.parentElement.parentElement;
  994. }, null, "bh_owned");
  995.  
  996. markOwned("div > p > a[href*='store.steampowered.com/']"
  997. , null, null, "bh_owned");
  998. }, 1000);
  999. }
  1000. else if (url.indexOf("groupees.com") > -1)
  1001. {
  1002. GM_addStyle(
  1003. " .bh_owned { background-color: #DFF0D8 !important; } "
  1004. + " #subscribe-form { display: none; } "
  1005. + " input[name='search'] { position: fixed; z-index: 1099; left: 18%; top: 16px; } "
  1006. + " button[role='show3dKeyModal'] { position: fixed; z-index: 1099; left: 72%; top: 16px; } "
  1007. + " .cancel-spin { position: fixed !important; z-index: 1099 !important; left: 39.5% !important; top: 22px !important; } "
  1008. + " .bh_owned_dark { background-color: rgba(140, 197, 63, 0.6) !important; } "
  1009. );
  1010. /*
  1011. var a = document.querySelector("input[name='search']");
  1012. var b = document.querySelector("#subscribe-form");
  1013. b.parentElement.insertBefore(a,b);
  1014. b.parentElement.removeChild(b);
  1015. */
  1016. // Add mark button
  1017. {
  1018. var divButton = document.createElement("div");
  1019. divButton.classList.add("bh_button");
  1020. divButton.id = "bh_markOwned";
  1021.  
  1022. var eleA = document.createElement("a");
  1023. eleA.setAttribute("onclick", "return false;");
  1024. eleA.textContent = "Mark Owned";
  1025.  
  1026. divButton.appendChild(eleA);
  1027. document.body.appendChild(divButton);
  1028.  
  1029. divButton.addEventListener("click", function()
  1030. {
  1031. var apps = [];
  1032.  
  1033. var eleApps = document.querySelectorAll(".bundle > .products .info .description a[href*='store.steampowered.com/app/']"
  1034. + ", .expanded .product-info a[href*='store.steampowered.com/app/']"
  1035. + ", .product-details.hidden .external-links a[href*='store.steampowered.com/app/']");
  1036. console.log("Apps: " + eleApps.length);
  1037.  
  1038. for (var i = 0; i < eleApps.length; i++)
  1039. {
  1040. var app = /[0-9]+/.exec(eleApps[i].getAttribute("href"));
  1041. if (app != null)
  1042. {
  1043. apps.push(app[0]);
  1044. }
  1045. }
  1046.  
  1047. apps = apps.filter(function(elem, index, self)
  1048. {
  1049. return index == self.indexOf(elem);
  1050. });
  1051.  
  1052. var appAll = apps.join(",");
  1053.  
  1054. GM_xmlhttpRequest(
  1055. {
  1056. method: "GET",
  1057. headers:
  1058. {
  1059. "Cache-Control": "max-age=0"
  1060. },
  1061. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  1062. onload: function(response)
  1063. {
  1064. var dataRes = JSON.parse(response.responseText);
  1065.  
  1066. var countOwned = 0;
  1067.  
  1068. var elePurchases = null;
  1069. var elePrds = document.querySelectorAll(".bundle > .products .product h3"
  1070. + ", .expanded .details, .product-info");
  1071. var eleApps = document.querySelectorAll(".bundle > .products .info"
  1072. + ", .expanded .details, .product-details.hidden");
  1073. for (var i = 0; i < eleApps.length; i++)
  1074. {
  1075. var eleApp = eleApps[i].querySelector(".description a[href*='store.steampowered.com/app/']"
  1076. + ", .product-info a[href*='store.steampowered.com/app/']"
  1077. + ", .external-links a[href*='store.steampowered.com/app/']");
  1078. if (eleApp != null)
  1079. {
  1080. var app = /[0-9]+/.exec(eleApp.getAttribute("href"));
  1081. if (app != null)
  1082. {
  1083. if (typeof dataRes[app] !== "undefined")
  1084. {
  1085. if (dataRes[app].success)
  1086. {
  1087. if (dataRes[app].data.is_owned)
  1088. {
  1089. var eleLabel = elePrds[i];
  1090. if (eleLabel.classList.contains("product-info"))
  1091. {
  1092. eleLabel.classList.add("bh_owned_dark");
  1093.  
  1094. // Mark game in build bundles
  1095. {
  1096. var eleName = eleLabel.querySelector("h4");
  1097. if (eleName)
  1098. {
  1099. var name = eleName.textContent.trim();
  1100.  
  1101. if (elePurchases == null)
  1102. {
  1103. elePurchases = document.querySelectorAll(".purchase-products > .bundle-product > .input > label");
  1104. }
  1105.  
  1106. if (elePurchases != null)
  1107. {
  1108. for (var j = 0; j < elePurchases.length; j++)
  1109. {
  1110. if (elePurchases[j].textContent.trim() == name)
  1111. {
  1112. elePurchases[j].parentElement.parentElement.classList.add("bh_owned_dark");
  1113. }
  1114. }
  1115. }
  1116. }
  1117. }
  1118.  
  1119. }
  1120. else
  1121. {
  1122. eleLabel.classList.add("bh_owned");
  1123. }
  1124. countOwned++;
  1125. }
  1126. else
  1127. {
  1128. console.log("App: not owned - http://store.steampowered.com/app/" + app);
  1129. }
  1130. }
  1131. else
  1132. {
  1133. console.log("App: not success - http://store.steampowered.com/app/" + app);
  1134. }
  1135. }
  1136. }
  1137. }
  1138. }
  1139.  
  1140. console.log("Apps: owned - " + countOwned);
  1141.  
  1142. } // End onload
  1143. });
  1144.  
  1145. });
  1146. }
  1147. }
  1148. else if (url.indexOf("indiegala.com") > -1)
  1149. {
  1150. GM_addStyle(
  1151. " .bh_owned, .bh_owned .bundle-item-trading { background-color: rgba(125, 174, 45, 0.9) !important; } "
  1152. + " .ig-bundle { padding-left: 3px; padding-right: 3px; margin-bottom: 3px; } "
  1153. + " .bh_owned.ig-bundle { background-color: rgba(125, 174, 45) !important; } "
  1154. + " .bh_owned.ig-bundle .bundle-item-trading { background-color: rgba(125, 174, 45, 0) !important; } "
  1155. + " .bh_owned .add-info-button-cont .left, .bh_owned .add-info-button-cont .palette-background-2 { "
  1156. + " background-color: #7DAE2D !important; } "
  1157. + " .bh_owned .add-info-button-cont .right .inner-info, .bh_owned .add-info-button-cont .right .palette-border-2 { "
  1158. + " border-color: #7DAE2D !important; } "
  1159. + " .bh_owned.medium-game .game-cover-medium { border: 3px solid #7DAE2D; background-color: rgba(125, 174, 45, 0.4); } "
  1160. + " .bh_owned.game-data-cont { background-color: #76AD1C !important; } "
  1161. + " .bundle-item-trading-cards-cont span { opacity: 0.7; } "
  1162. + " .span-title .title_game, .span-title .title_drm, .span-title .title_music { "
  1163. + " line-height: 43px !important; margin: 10px 0px 10px 15px !important; "
  1164. + " padding-left: 10px !important; border-radius: 3px !important; } "
  1165. + " .medium-game { min-height: 146px; } "
  1166. );
  1167.  
  1168. // Auto reload when error
  1169. {
  1170. setTimeout(function()
  1171. {
  1172. if (document.title == "500 Server Error")
  1173. {
  1174. console.log("Autorefresh: 500 Server Error");
  1175. setTimeout(function()
  1176. {
  1177. reload();
  1178. }, 5000);
  1179. }
  1180. }, 10000);
  1181. }
  1182.  
  1183. // Insert email to bundle section
  1184. {
  1185. var countRetryEmail = 10;
  1186. var tmRetryEmail = setInterval(function()
  1187. {
  1188. var eleEmail = document.querySelector(".account-email");
  1189. var eleInput = document.querySelector(".email-input");
  1190. if (eleEmail && eleInput)
  1191. {
  1192. var email = eleEmail.textContent.trim();
  1193. if (email != "")
  1194. {
  1195. eleInput.value = email;
  1196. clearInterval(tmRetryEmail);
  1197. }
  1198. }
  1199.  
  1200. if (countRetryEmail < 0)
  1201. {
  1202. clearInterval(tmRetryEmail);
  1203. }
  1204. countRetryEmail--;
  1205. }, 3000);
  1206. }
  1207.  
  1208. // Add mark button
  1209. if (url.indexOf("/store/product/") < 0)
  1210. {
  1211. var divButton = document.createElement("div");
  1212. divButton.classList.add("bh_button");
  1213. divButton.id = "bh_markOwned";
  1214.  
  1215. var eleA = document.createElement("a");
  1216. eleA.setAttribute("onclick", "return false;");
  1217. eleA.textContent = "Mark Owned";
  1218.  
  1219. divButton.appendChild(eleA);
  1220. document.body.appendChild(divButton);
  1221.  
  1222. divButton.addEventListener("click", function()
  1223. {
  1224. var rgxId = /[0-9]{3,}/g;
  1225. var rgxApp = /:\/\/((store\.steampowered\.com|steamcommunity\.com)\/app|www.indiegala.com\/store\/product\/[^\/]+)\/[0-9]+/i;
  1226. var rgxSub = /:\/\/(store\.steampowered\.com|steamcommunity\.com)\/sub\/[0-9]+/i;
  1227. var rgxInvalidApp = /:\/\/store\.steampowered\.com\/[0-9]+\//i;
  1228.  
  1229. var eleApps = document.querySelectorAll("a[href*='store.steampowered.com/']");
  1230. for (var i = 0; i < eleApps.length; i++)
  1231. {
  1232. var attrHref = eleApps[i].getAttribute("href");
  1233. if (rgxInvalidApp.test(attrHref))
  1234. {
  1235. eleApps[i].setAttribute("href", attrHref.replace("store.steampowered.com/", "store.steampowered.com/app/"));
  1236. }
  1237. }
  1238.  
  1239. GM_xmlhttpRequest(
  1240. {
  1241. method: "GET",
  1242. url: "https://store.steampowered.com/dynamicstore/userdata/?t=" + getUnixTimestamp(),
  1243. onload: function(response)
  1244. {
  1245. var dataRes = JSON.parse(response.responseText);
  1246.  
  1247. var countOwned = 0;
  1248.  
  1249. if (typeof dataRes["rgOwnedApps"] !== "undefined"
  1250. && typeof dataRes["rgOwnedPackages"] !== "undefined"
  1251. && typeof dataRes["rgIgnoredApps"] !== "undefined")
  1252. {
  1253. var elePrds = document.querySelectorAll(
  1254. ".bundle-item-link, .in .in .in .game-steam-url"
  1255. + ", #this_your_gift .game-steam-url");
  1256. var eleApps = document.querySelectorAll(
  1257. ".game-opened-switcher a[href*='store.steampowered.com/'].game-steam-url"
  1258. + ", .game-opened-switcher a[href*='steamcommunity.com/'].game-steam-url"
  1259. + ", .bundle-item-header a[href*='store.steampowered.com/'].game-steam-url"
  1260. + ", .bundle-item-header a[href*='steamcommunity.com/'].game-steam-url"
  1261. + ", .in .in .in .game-steam-url"
  1262. + ", #this_your_gift .game-steam-url"
  1263. + ", .game-cover-medium[href^='/store/product/']"
  1264. + ", #game_list_div .game-data-cont a[href^='/store/product/']"
  1265. + ", .search-page-store .game-data-cont a[href^='/store/product/']");
  1266.  
  1267. var eleTitle = document.querySelector("#title-p");
  1268. if (eleTitle && eleTitle.textContent.trim() === "Indiegala gift")
  1269. {
  1270. elePrds = document.querySelectorAll(
  1271. "#steam-key-games .game-steam-url");
  1272. eleApps = document.querySelectorAll(
  1273. "#steam-key-games .game-steam-url");
  1274. }
  1275.  
  1276.  
  1277. for (var i = 0; i < eleApps.length; i++)
  1278. {
  1279. var attrHref = eleApps[i].href;
  1280. var ids = attrHref.match(rgxId);
  1281. if (ids != null)
  1282. {
  1283. var valId = parseInt(ids[ids.length - 1]);
  1284.  
  1285. var eleLabel = null;
  1286.  
  1287. if (eleApps[i].classList.contains("game-cover-medium"))
  1288. {
  1289. eleLabel = eleApps[i].parentElement;
  1290. }
  1291. else if (eleApps[i].parentElement.parentElement.classList.contains("game-data-cont"))
  1292. {
  1293. eleLabel = eleApps[i].parentElement.parentElement;
  1294. }
  1295. else if (elePrds[i].classList.contains("bundle-item-link"))
  1296. {
  1297. eleLabel = elePrds[i].parentElement.parentElement;
  1298. }
  1299. else
  1300. {
  1301. eleLabel = elePrds[i].parentElement;
  1302. }
  1303.  
  1304. if (rgxApp.test(attrHref))
  1305. {
  1306. if (dataRes["rgOwnedApps"].indexOf(valId) > -1)
  1307. {
  1308. eleLabel.classList.add("bh_owned");
  1309. countOwned++;
  1310. }
  1311. else
  1312. {
  1313. console.log("App: not owned - http://store.steampowered.com/app/" + valId);
  1314. }
  1315. }
  1316. else if (rgxSub.test(attrHref))
  1317. {
  1318. if (dataRes["rgOwnedPackages"].indexOf(valId) > -1)
  1319. {
  1320. eleLabel.classList.add("bh_owned");
  1321. countOwned++;
  1322. }
  1323. else
  1324. {
  1325. console.log("Sub: not owned - http://store.steampowered.com/sub/" + valId);
  1326. }
  1327. }
  1328. }
  1329. }
  1330.  
  1331. console.log("Apps: owned - " + countOwned);
  1332. }
  1333. } // End onload
  1334. });
  1335.  
  1336. });
  1337. }
  1338.  
  1339. // Change title
  1340. {
  1341. var countRetryTitle = 10;
  1342. var tmRetryTitle = setInterval(function()
  1343. {
  1344. var elesPrice = document.querySelectorAll(".bundle-claim-phrase");
  1345. for (var i = elesPrice.length - 1; i > -1; i--)
  1346. {
  1347. var elePrice = elesPrice[i].querySelector("span");
  1348. if (elePrice)
  1349. {
  1350. var price = elePrice.textContent.trim();
  1351. if (price.indexOf("$") == 0)
  1352. {
  1353. document.title = price + " " + document.title;
  1354. clearInterval(tmRetryTitle);
  1355. break;
  1356. }
  1357. }
  1358. }
  1359.  
  1360. if (countRetryTitle < 0)
  1361. {
  1362. clearInterval(tmRetryTitle);
  1363. }
  1364. countRetryTitle--;
  1365. }, 3000);
  1366. }
  1367.  
  1368. // Load library
  1369. if (url.indexOf("/profile") > -1)
  1370. {
  1371. var clientScript = " \
  1372. function openBundleLibrary() \
  1373. { \
  1374. $.ajax({ \
  1375. type: 'GET', \
  1376. data: { user_id : '" + getQueryByName("user_id") + "' }, \
  1377. url: '/ajaxprofile_sale_tab', \
  1378. dataType: 'json', \
  1379. context: $( '#profile_bundle_section .accordion-toggle' ), \
  1380. \
  1381. beforeSend: function(){ \
  1382. console.log('Start: open bundle library' ); \
  1383. openBundleLibraryAjaxSemaphore = false; \
  1384. $('.spinner', $( '#profile_bundle_section .accordion-toggle' ) ).remove(); \
  1385. $( '#profile_bundle_section .accordion-toggle' ).append(\" <span class='spinner'><i class='fa fa-spinner fa-spin'></i></span>\"); \
  1386. }, \
  1387. success: function(data){ \
  1388. console.log('Success: open bundle library' ); \
  1389. $( '#collapseBundles .panel-body' ).html( data['html'] ); \
  1390. setTimeout(function() \
  1391. { \
  1392. $('#profile_bundle_section .accordion-toggle:not([aria-expanded=\"true\"])').click(); \
  1393. }, 500); \
  1394. }, \
  1395. error: function(){ \
  1396. console.log('Error: open bundle library' ); \
  1397. setTimeout(openBundleLibrary, 500); \
  1398. }, \
  1399. complete: function(){ \
  1400. openBundleLibraryAjaxSemaphore = false; \
  1401. $('.spinner', $( '#profile_bundle_section .accordion-toggle' ) ).remove(); \
  1402. }, \
  1403. }); \
  1404. } \
  1405. setTimeout(openBundleLibrary, 1000); \
  1406. ";
  1407.  
  1408. var eleClientScript = document.createElement("script");
  1409. eleClientScript.innerHTML = clientScript;
  1410. document.head.appendChild(eleClientScript);
  1411.  
  1412. var divButton = document.createElement("div");
  1413. divButton.classList.add("bh_button");
  1414. divButton.id = "bh_OpenLib";
  1415. divButton.setAttribute("onclick", "openBundleLibrary()");
  1416.  
  1417. var eleA = document.createElement("a");
  1418. eleA.setAttribute("onclick", "return false;");
  1419. eleA.textContent = "Open Library";
  1420.  
  1421. divButton.appendChild(eleA);
  1422. document.body.appendChild(divButton);
  1423. }
  1424.  
  1425. if (url.indexOf("/successpay") > -1 || url.indexOf("/givmessage?message=ok") > -1)
  1426. {
  1427. setTimeout(function()
  1428. {
  1429. window.location = "/profile";
  1430. /*
  1431. var eleBtn = document.querySelector("#faang.fa-angle-down");
  1432. if (eleBtn)
  1433. {
  1434. eleBtn.click();
  1435. }
  1436. */
  1437. }, 10000);
  1438. }
  1439. }
  1440. else if (url.indexOf("orlygift.com") > -1)
  1441. {
  1442. if (window !== window.parent)
  1443. return;
  1444.  
  1445. // Add mark button
  1446. {
  1447. var divButton = document.createElement("div");
  1448. divButton.classList.add("bh_button");
  1449. divButton.id = "bh_markOwned";
  1450.  
  1451. var eleA = document.createElement("a");
  1452. eleA.textContent = "Mark Owned";
  1453.  
  1454. divButton.appendChild(eleA);
  1455. document.body.appendChild(divButton);
  1456.  
  1457. divButton.addEventListener("click", function()
  1458. {
  1459. var apps = [];
  1460.  
  1461. var eleApps = document.querySelectorAll("div[id^='game-detail-'] a.btn-primary[href^='http://store.steampowered.com/app/']");
  1462. console.log("Apps: " + eleApps.length);
  1463.  
  1464. for (var i = 0; i < eleApps.length; i++)
  1465. {
  1466. var app = /[0-9]+/.exec(eleApps[i].getAttribute("href"));
  1467. if (app != null)
  1468. {
  1469. apps.push(app[0]);
  1470. }
  1471. }
  1472.  
  1473. apps = apps.filter(function(elem, index, self)
  1474. {
  1475. return index == self.indexOf(elem);
  1476. });
  1477.  
  1478. var appAll = apps.join(",");
  1479.  
  1480. GM_xmlhttpRequest(
  1481. {
  1482. method: "GET",
  1483. headers:
  1484. {
  1485. "Cache-Control": "max-age=0"
  1486. },
  1487. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  1488. onload: function(response)
  1489. {
  1490. var dataRes = JSON.parse(response.responseText);
  1491.  
  1492. var countOwned = 0;
  1493.  
  1494. var elePrds = document.querySelectorAll(".box-game");
  1495. var eleApps = document.querySelectorAll("div[id^='game-detail-']");
  1496. for (var i = 0; i < eleApps.length; i++)
  1497. {
  1498. var eleApp = eleApps[i].querySelector("a.btn-primary[href^='http://store.steampowered.com/app/']");
  1499. if (eleApp != null)
  1500. {
  1501. var app = /[0-9]+/.exec(eleApp.getAttribute("href"));
  1502. if (app != null)
  1503. {
  1504. if (typeof dataRes[app] !== "undefined")
  1505. {
  1506. if (dataRes[app].success)
  1507. {
  1508. if (dataRes[app].data.is_owned)
  1509. {
  1510. var eleLabel = elePrds[i];
  1511. eleLabel.classList.add("bh_owned");
  1512. countOwned++;
  1513. }
  1514. else
  1515. {
  1516. console.log("App: not owned - " + app);
  1517. }
  1518. }
  1519. else
  1520. {
  1521. console.log("App: not success - " + app);
  1522. }
  1523. }
  1524. }
  1525. }
  1526. }
  1527.  
  1528. console.log("Apps: owned - " + countOwned);
  1529.  
  1530. } // End onload
  1531. });
  1532.  
  1533. });
  1534. }
  1535. }
  1536. else if (url.indexOf("cubicbundle.com") > -1)
  1537. {
  1538. GM_addStyle(
  1539. " .bh_owned { background-color: #91BA07 !important; } "
  1540. );
  1541. // Add mark button
  1542. {
  1543. var divButton = document.createElement("div");
  1544. divButton.classList.add("bh_button");
  1545. divButton.id = "bh_markOwned";
  1546.  
  1547. var eleA = document.createElement("a");
  1548. eleA.setAttribute("onclick", "return false;");
  1549. eleA.textContent = "Mark Owned";
  1550.  
  1551. divButton.appendChild(eleA);
  1552. document.body.appendChild(divButton);
  1553.  
  1554. divButton.addEventListener("click", function()
  1555. {
  1556. markOwned(".price a[href*='store.steampowered.com/']", function(ele)
  1557. {
  1558. return ele.parentElement.parentElement.parentElement.parentElement.parentElement;
  1559. }, null, "bh_owned");
  1560. });
  1561. }
  1562. }
  1563. else if (url.indexOf("dailyindiegame.com") > -1)
  1564. {
  1565. GM_addStyle(
  1566. " .bh_owned, .bh_owned a, .bh_owned a:not(:visited) .DIG2content { color: #202020 !important; } "
  1567. );
  1568.  
  1569. // Add mark button
  1570. {
  1571. var divButton = document.createElement("div");
  1572. divButton.classList.add("bh_button");
  1573. divButton.id = "bh_markOwned";
  1574.  
  1575. var eleA = document.createElement("a");
  1576. eleA.setAttribute("onclick", "return false;");
  1577. eleA.textContent = "Mark Owned";
  1578.  
  1579. divButton.appendChild(eleA);
  1580. document.body.appendChild(divButton);
  1581.  
  1582. divButton.addEventListener("click", function()
  1583. {
  1584. if (document.querySelectorAll(".DIG-content a[href*='store.steampowered.com/']").length > 0)
  1585. {
  1586. markOwned(".DIG-content a[href*='store.steampowered.com/']", function(ele)
  1587. {
  1588. return ele.parentElement
  1589. .parentElement.parentElement
  1590. .parentElement.parentElement;
  1591. }, null, "bh_owned");
  1592. }
  1593. else
  1594. {
  1595. markOwned(".DIG2content a[href*='store.steampowered.com/']", function(ele)
  1596. {
  1597. return ele.parentElement.parentElement;
  1598. }, null, "bh_owned");
  1599.  
  1600. markOwned(".DIG3_14_Gray a[href*='store.steampowered.com/']", function(ele)
  1601. {
  1602. return ele.parentElement.parentElement.parentElement;
  1603. }, null, "bh_owned");
  1604. }
  1605. });
  1606. }
  1607. }
  1608. else if (url.indexOf("bundlekings.com") > -1)
  1609. {
  1610. // Add mark button
  1611. {
  1612. var divButton = document.createElement("div");
  1613. divButton.classList.add("bh_button");
  1614. divButton.id = "bh_markOwned";
  1615.  
  1616. var eleA = document.createElement("a");
  1617. eleA.setAttribute("onclick", "return false;");
  1618. eleA.textContent = "Mark Owned";
  1619.  
  1620. divButton.appendChild(eleA);
  1621. document.body.appendChild(divButton);
  1622.  
  1623. divButton.addEventListener("click", function()
  1624. {
  1625. markOwned(".content-wrap a[href*='store.steampowered.com/']", function(ele)
  1626. {
  1627. return ele.parentElement.parentElement.parentElement;
  1628. }, null, "bh_owned");
  1629. });
  1630. }
  1631. }
  1632. else if (url.indexOf("otakumaker.com") > -1)
  1633. {
  1634. GM_addStyle(
  1635. " .bh_owned { background-color: #91BA07 !important; } "
  1636. );
  1637.  
  1638. // Add mark button
  1639. {
  1640. var divButton = document.createElement("div");
  1641. divButton.classList.add("bh_button");
  1642. divButton.id = "bh_markOwned";
  1643.  
  1644. var eleA = document.createElement("a");
  1645. eleA.setAttribute("onclick", "return false;");
  1646. eleA.textContent = "Mark Owned";
  1647.  
  1648. divButton.appendChild(eleA);
  1649. document.body.appendChild(divButton);
  1650.  
  1651. divButton.addEventListener("click", function()
  1652. {
  1653. markOwned(".gantry-width-spacer a[href*='store.steampowered.com/']", function(ele)
  1654. {
  1655. return ele.parentElement.parentElement;
  1656. }, null, "bh_owned");
  1657. });
  1658. }
  1659. }
  1660. else if (url.indexOf("otakubundle.com") > -1)
  1661. {
  1662. GM_addStyle(
  1663. " .bh_owned { background-color: #91BA07 !important; } "
  1664. );
  1665.  
  1666. // Add mark button
  1667. {
  1668. var divButton = document.createElement("div");
  1669. divButton.classList.add("bh_button");
  1670. divButton.id = "bh_markOwned";
  1671.  
  1672. var eleA = document.createElement("a");
  1673. eleA.setAttribute("onclick", "return false;");
  1674. eleA.textContent = "Mark Owned";
  1675.  
  1676. divButton.appendChild(eleA);
  1677. document.body.appendChild(divButton);
  1678.  
  1679. divButton.addEventListener("click", function()
  1680. {
  1681. markOwned("#hikashop_product_left_part > .g-grid > .g-block "
  1682. + " > .g-block > a[href*='store.steampowered.com/']", function(ele)
  1683. {
  1684. return ele.parentElement.parentElement;
  1685. }, null, "bh_owned");
  1686. });
  1687. }
  1688. }
  1689. else if (url.indexOf("gogobundle.com") > -1)
  1690. {
  1691. GM_addStyle(
  1692. " .bh_owned { background-color: #91BA07 !important; border: 1px solid white; } "
  1693. );
  1694.  
  1695. // Add mark button
  1696. {
  1697. var divButton = document.createElement("div");
  1698. divButton.classList.add("bh_button");
  1699. divButton.id = "bh_markOwned";
  1700.  
  1701. var eleA = document.createElement("a");
  1702. eleA.setAttribute("onclick", "return false;");
  1703. eleA.textContent = "Mark Owned";
  1704.  
  1705. divButton.appendChild(eleA);
  1706. document.body.appendChild(divButton);
  1707.  
  1708. divButton.addEventListener("click", function()
  1709. {
  1710. markOwned(".g-block > .g-block > a[href*='store.steampowered.com/']", function(ele)
  1711. {
  1712. return ele.parentElement.parentElement;
  1713. }, null, "bh_owned");
  1714. });
  1715. }
  1716. }
  1717. else if (url.indexOf("superduperbundle.com") > -1)
  1718. {
  1719. // Add mark button
  1720. {
  1721. var divButton = document.createElement("div");
  1722. divButton.classList.add("bh_button");
  1723. divButton.id = "bh_markOwned";
  1724.  
  1725. var eleA = document.createElement("a");
  1726. eleA.setAttribute("onclick", "return false;");
  1727. eleA.textContent = "Mark Owned";
  1728.  
  1729. divButton.appendChild(eleA);
  1730. document.body.appendChild(divButton);
  1731.  
  1732. divButton.addEventListener("click", function()
  1733. {
  1734. markOwned("#gameslist a[href*='store.steampowered.com/']", function(ele)
  1735. {
  1736. return ele.parentElement.parentElement;
  1737. }, null, "bh_owned");
  1738. });
  1739. }
  1740. }
  1741. else if (url.indexOf("gamebundle.com") > -1)
  1742. {
  1743. GM_addStyle(
  1744. " .bh_owned { background-color: #A0CC41 !important; border-bottom: 45px solid rgba(233, 233, 233, 0.5); } "
  1745. + " .bh_owned .activebundle_game_bundle_debut_title { background-color: #A0CC41 !important; } "
  1746. );
  1747.  
  1748. // Add mark button
  1749. {
  1750. var divButton = document.createElement("div");
  1751. divButton.classList.add("bh_button");
  1752. divButton.id = "bh_markOwned";
  1753.  
  1754. var eleA = document.createElement("a");
  1755. eleA.setAttribute("onclick", "return false;");
  1756. eleA.textContent = "Mark Owned";
  1757.  
  1758. divButton.appendChild(eleA);
  1759. document.body.appendChild(divButton);
  1760.  
  1761. divButton.addEventListener("click", function()
  1762. {
  1763. markOwned(".activebundle_game_section_full a[href*='store.steampowered.com/']", function(ele)
  1764. {
  1765. return ele.parentElement;
  1766. }, null, "bh_owned");
  1767. });
  1768. }
  1769. }
  1770. else if (url.indexOf("humblebundle.com") > -1)
  1771. {
  1772. GM_addStyle(
  1773. " .game-box img { max-height: 180px !important; max-width: 130px !important; } "
  1774. );
  1775.  
  1776. var rgxBta = /Pay more than the average of (.[0-9\.]+).+/i;
  1777. var rgxFix = /Pay (.[0-9\.]+) or more to also unlock!/i;
  1778. var strPay = "Pay ";
  1779. var price = "";
  1780.  
  1781. var elesPrice = document.querySelectorAll(".dd-header-headline");
  1782. for (var i = 0; i < elesPrice.length; i++)
  1783. {
  1784. var priceRaw = elesPrice[i].textContent.trim();
  1785. if (rgxBta.test(priceRaw))
  1786. {
  1787. price = priceRaw.replace(rgxBta, "$1");
  1788. break;
  1789. }
  1790. else if (rgxFix.test(priceRaw))
  1791. {
  1792. price = priceRaw.replace(rgxFix, "$1");
  1793. break;
  1794. }
  1795. }
  1796.  
  1797. if (!price && elesPrice.length >= 3 && elesPrice.length <= 4)
  1798. {
  1799. var priceRaw = elesPrice[1].textContent.trim();
  1800. if (priceRaw.indexOf(strPay) > -1)
  1801. {
  1802. price = priceRaw.replace(strPay, "").replace("+", "");
  1803. }
  1804. }
  1805.  
  1806. if (price)
  1807. {
  1808. document.title = price + " " + document.title;
  1809. }
  1810.  
  1811. var eleSold = document.querySelector(".heading-bundles-sold .mini-digit-holder");
  1812. if (eleSold != null)
  1813. {
  1814. var sold = eleSold.getAttribute("data-initial-value") || "";
  1815. eleSold.parentElement.parentElement.setAttribute("title", sold);
  1816. }
  1817.  
  1818. var countRetrySold = 10;
  1819. var tmRetrySold = setInterval(function()
  1820. {
  1821. var sold = "";
  1822. var elesSold = document.querySelectorAll(".hr-main-container .mini-digit-holder .top-cutter .heading-num");
  1823. for (var i = 0; i < elesSold.length; i++)
  1824. {
  1825. sold = sold + "" + elesSold[i].textContent.trim();
  1826. }
  1827.  
  1828. if (sold !== "")
  1829. {
  1830. clearInterval(tmRetrySold);
  1831.  
  1832. var eleCount = document.querySelector(".hr-tagline-bundles-sold");
  1833. if (eleCount)
  1834. {
  1835. eleCount.textContent += " (" + sold.replace(/^0+/, "") + ")";
  1836. }
  1837. }
  1838.  
  1839. if (countRetrySold < 0)
  1840. {
  1841. clearInterval(tmRetrySold);
  1842. }
  1843. countRetrySold--;
  1844. }, 1000);
  1845.  
  1846. if (url.indexOf("/downloads") > -1)
  1847. {
  1848. GM_addStyle(
  1849. " #steam-tab .redeem-instructions, #steam-tab .recommend-this-game { display: none; } "
  1850. + " #papers-content { margin-bottom: 300px; } "
  1851. );
  1852.  
  1853. attachOnLoad(function()
  1854. {
  1855. setTimeout(function()
  1856. {
  1857. var eleFocus = document.querySelector(".expiration-messaging");
  1858. if (eleFocus)
  1859. {
  1860. eleFocus.parentElement.scrollIntoView();
  1861. window.scrollBy(0, -100);
  1862.  
  1863.  
  1864. var eleClick = document.querySelector(".keyfield-value");
  1865. if (eleClick)
  1866. {
  1867. eleClick.click();
  1868. }
  1869. }
  1870.  
  1871. }, 2000);
  1872. });
  1873.  
  1874. /*setTimeout(function()
  1875. {
  1876. var elesKey = document.querySelectorAll(".sr-redeemed-bubble");
  1877. for (var i = 0; i < elesKey.length; i++)
  1878. {
  1879. elesKey[i].addEventListener("click", function (e)
  1880. {
  1881. var ele = e.target;
  1882. clickToSelect(ele);
  1883. });
  1884. }
  1885. }, 3000);*/
  1886. }
  1887. else if (url.indexOf("/receipt") > -1)
  1888. {
  1889. setTimeout(function()
  1890. {
  1891. var eleClick = document.querySelector("a[href*='/?key=']");
  1892. if (eleClick)
  1893. {
  1894. eleClick.click();
  1895. }
  1896. }, 10000);
  1897. }
  1898. else if (url.indexOf("/emailhelper") > -1)
  1899. {
  1900. setTimeout(function()
  1901. {
  1902. var eleClicks = document.querySelectorAll("div[name='order-link-btn'] a");
  1903. if (eleClicks.length === 1)
  1904. {
  1905. eleClicks[0].click();
  1906. }
  1907. }, 3000);
  1908. }
  1909. else if (url.indexOf("/store/") > -1)
  1910. {
  1911. setTimeout(function()
  1912. {
  1913. var elePrice = document.querySelector(".current-price.free");
  1914. if (elePrice)
  1915. {
  1916. scrollToElement(elePrice, -500);
  1917. }
  1918. }, 3000);
  1919. }
  1920. }
  1921. else if (url.indexOf("steamcompanion.com") > -1)
  1922. {
  1923. GM_addStyle(
  1924. " .bh_owned.banner { margin-bottom: 5px !important; margin-top: 35px !important; "
  1925. + " padding-bottom: 15px !important; padding-top: 15px !important; } "
  1926. + " .bh_owned.giveaway-links { opacity: 0.75; } "
  1927. );
  1928.  
  1929. markOwned("#hero a[href*='store.steampowered.com/']"
  1930. , null, null, "bh_owned");
  1931.  
  1932. // Mark
  1933. {
  1934. var query = ".giveaway-links img[src^='https://steamcdn-a.akamaihd.net/steam/apps/']";
  1935. var getLabelCallback = function(ele)
  1936. {
  1937. return ele.parentElement.parentElement.parentElement;
  1938. };
  1939.  
  1940. var apps = [];
  1941.  
  1942. var eleApps = document.querySelectorAll(query);
  1943.  
  1944. for (var i = 0; i < eleApps.length; i++)
  1945. {
  1946. var app = /[0-9]+/.exec(eleApps[i].getAttribute("src"));
  1947. if (app != null)
  1948. {
  1949. apps.push(app[0]);
  1950. }
  1951. }
  1952.  
  1953. apps = apps.filter(function(elem, index, self)
  1954. {
  1955. return index == self.indexOf(elem);
  1956. });
  1957.  
  1958. console.log("Apps: " + apps.length);
  1959. var appAll = apps.join(",");
  1960.  
  1961. GM_xmlhttpRequest(
  1962. {
  1963. method: "GET",
  1964. headers:
  1965. {
  1966. "Cache-Control": "max-age=0"
  1967. },
  1968. url: "https://store.steampowered.com/api/appuserdetails/?appids=" + appAll,
  1969. onload: function(response)
  1970. {
  1971. var dataRes = JSON.parse(response.responseText);
  1972.  
  1973. var countOwned = 0;
  1974.  
  1975. var eleApps = document.querySelectorAll(query);
  1976. for (var i = 0; i < eleApps.length; i++)
  1977. {
  1978. var appUrl = eleApps[i].getAttribute("src");
  1979. if (appUrl.indexOf("https://steamcdn-a.akamaihd.net/steam/apps/") > -1)
  1980. {
  1981. var app = /[0-9]+/.exec(appUrl);
  1982. if (app != null)
  1983. {
  1984. if (typeof dataRes[app] !== "undefined")
  1985. {
  1986. if (dataRes[app].success)
  1987. {
  1988. if (dataRes[app].data.is_owned)
  1989. {
  1990. var eleLabel = getLabelCallback(eleApps[i]);
  1991. eleLabel.classList.add("bh_owned");
  1992. countOwned++;
  1993. }
  1994. else
  1995. {
  1996. //console.log("App: not owned - http://store.steampowered.com/app/" + app + "/");
  1997. }
  1998. }
  1999. else
  2000. {
  2001. //console.log("App: not success - https://steamdb.info/app/" + app + "/");
  2002. }
  2003. }
  2004. }
  2005. }
  2006. }
  2007.  
  2008. console.log("Apps: owned - " + countOwned);
  2009.  
  2010. } // End onload
  2011. });
  2012. }
  2013. }
  2014. else if (url.indexOf("store.steampowered.com") > -1)
  2015. {
  2016. if (url.indexOf("/widget/") > -1)
  2017. {
  2018. GM_addStyle(
  2019. " .bh_owned { background-color: transparent !important; } "
  2020. + " .bh_owned a { color: #71A034 !important; }"
  2021. );
  2022.  
  2023. markOwned(".main_text a[href*='store.steampowered.com/']", function(ele)
  2024. {
  2025. return ele.parentElement;
  2026. }, null, "bh_owned");
  2027. }
  2028. else if (url.indexOf("/app/") > -1)
  2029. {
  2030. GM_addStyle(
  2031. " .bh_owned { "
  2032. + " background-color: #6D8C1A !important; "
  2033. + " padding: 0px 2px 0px 2px; "
  2034. + " } "
  2035. );
  2036.  
  2037. markOwned(".glance_details p > a[href*='store.steampowered.com/']"
  2038. + ", .game_area_dlc_bubble a[href*='store.steampowered.com/']"
  2039. , null, null, "bh_owned");
  2040. }
  2041. else if (url.indexOf("/notinterested/") > -1)
  2042. {
  2043. GM_addStyle(
  2044. " .bh_owned { "
  2045. + " background-color: #6D8C1A !important; "
  2046. + " padding: 5px 100px 5px 5px !important; "
  2047. + " margin-left: -5px; margin-right: 50px; "
  2048. + " } "
  2049. );
  2050.  
  2051. markOwned(".ignoredapps > a[href*='store.steampowered.com/']"
  2052. , null, null, "bh_owned");
  2053. }
  2054. else if (url.indexOf("/search/") > -1)
  2055. {
  2056. GM_addStyle(
  2057. " .bh_owned { "
  2058. + " background-color: #6D8C1A66 !important; "
  2059. + " } "
  2060. );
  2061.  
  2062. markOwned(".search_result_row[href*='store.steampowered.com/']"
  2063. , null, null, "bh_owned");
  2064. }
  2065. }
  2066. else if (url.indexOf("steamcommunity.com") > -1)
  2067. {
  2068. GM_addStyle(
  2069. " .bh_owned { background-color: #71A034 !important; "
  2070. + " padding: 0px 2px 0px 2px; } "
  2071. + " .bh_owned.blotter_userstatus_game { padding: 0px; border-color: #71A034; } "
  2072. );
  2073.  
  2074. if (url.indexOf("/home") > -1)
  2075. {
  2076. var querySteamHome = ".blotter_gamepurchase_details a[href*='store.steampowered.com/']:not(.bh_owned) "
  2077. + ", .blotter_author_block a[href*='store.steampowered.com/']:not(.bh_owned) "
  2078. + ", .blotter_author_block a[href*='steamcommunity.com/app/']:not(.bh_owned) "
  2079. + ", .blotter_daily_rollup_line a[href*='steamcommunity.com/app/']:not(.bh_owned) ";
  2080. markOwned(querySteamHome, function(ele, type)
  2081. {
  2082. if (type === 1)
  2083. {
  2084. if (ele.classList.contains("blotter_userstats_game"))
  2085. {
  2086. ele.parentElement.classList.add("bh_owned");
  2087. }
  2088. else
  2089. {
  2090. ele.classList.add("bh_owned");
  2091. }
  2092. }
  2093. });
  2094.  
  2095. var targetObMark = document.getElementById("blotter_content");
  2096. if (targetObMark)
  2097. {
  2098. var tmObMark = -1;
  2099. var obMark = new MutationObserver(function(mutations)
  2100. {
  2101. mutations.forEach(function(mutation)
  2102. {
  2103. clearTimeout(tmObMark);
  2104. tmObMark = setTimeout(function(querySteamHome)
  2105. {
  2106. markOwned(querySteamHome, function(ele, type)
  2107. {
  2108. if (type === 1 && !ele.classList.contains("blotter_userstats_game"))
  2109. {
  2110. ele.classList.add("bh_owned");
  2111. }
  2112. });
  2113.  
  2114. }, 100, querySteamHome);
  2115. });
  2116. });
  2117.  
  2118. var configObMark = { childList: true };
  2119. obMark.observe(targetObMark, configObMark);
  2120. }
  2121. }
  2122. else if (url.indexOf("/announcements") > -1)
  2123. {
  2124. markOwned(".announcement_body a[href*='store.steampowered.com/']"
  2125. , null, null, "bh_owned");
  2126. }
  2127. }
  2128. else if (url.indexOf("forums.steampowered.com") > -1)
  2129. {
  2130. GM_addStyle(
  2131. " .bh_owned { background-color: #71A034 !important; "
  2132. + " padding: 0px 2px 0px 2px;"
  2133. + " } "
  2134. );
  2135.  
  2136. markOwned("div[id^='post_message'] a[href*='store.steampowered.com/']"
  2137. , null, null, "bh_owned");
  2138. }
  2139. else if (url.indexOf("whosgamingnow.net") > -1)
  2140. {
  2141. if (url.indexOf("/discussion") > -1)
  2142. {
  2143. GM_addStyle(
  2144. " .bh_owned { "
  2145. + " padding: 0px 2px 0px 2px;"
  2146. + " } "
  2147. );
  2148.  
  2149. markOwned(".MessageList a[href*='store.steampowered.com/']"
  2150. , null, null, "bh_owned");
  2151. }
  2152. else if (url.indexOf("/redeem") > -1)
  2153. {
  2154. GM_addStyle(
  2155. " .bh_owned { "
  2156. + " border: 1px solid #FFF;"
  2157. + " } "
  2158. + " .bh_owned .BoxArt { "
  2159. + " border: 0px !important;"
  2160. + " } "
  2161. );
  2162.  
  2163. markOwned(".GameInfo a[href*='store.steampowered.com/']", function(ele)
  2164. {
  2165. return ele.parentElement.parentElement.parentElement;
  2166. });
  2167. }
  2168. else if (url.indexOf("/giveaway") > -1)
  2169. {
  2170. GM_addStyle(
  2171. " .bh_owned { "
  2172. + " border: 5px solid #7CA156;"
  2173. + " } "
  2174. );
  2175.  
  2176. markOwned("img[src*='://cdn.akamai.steamstatic.com/steam/']"
  2177. , null, null, "bh_owned");
  2178. }
  2179. }
  2180. else if (url.indexOf("steamground.com") > -1 && url.indexOf("/wholesale") > -1)
  2181. {
  2182. GM_addStyle(
  2183. " .bh_owned { background-color: #48B24B !important; } "
  2184. + " .bh_owned .wholesale-card_title { color: #373d41 !important; } "
  2185. + " .bh_steam { display: none; } "
  2186. );
  2187.  
  2188. var elesTitle = document.querySelectorAll(".wholesale-card_title");
  2189. if (elesTitle.length > 0)
  2190. {
  2191. GM_xmlhttpRequest(
  2192. {
  2193. method: "GET",
  2194. url: "https://www.steamgifts.com/discussion/iy081/steamground-wholesale-build-a-bundle",
  2195. onload: function(response)
  2196. {
  2197. var data = response.responseText;
  2198. var eleContainer = document.createElement("div");
  2199. eleContainer.innerHTML = data;
  2200.  
  2201. var eleComment = eleContainer.querySelector(".comment__description");
  2202. if (eleComment)
  2203. {
  2204. var elesGame = eleComment.querySelectorAll("table td:nth-child(1) a[href*='store.steampowered.com/']");
  2205. if (elesGame.length > 0)
  2206. {
  2207. var arrTitle = [];
  2208. for (var i = 0; i < elesTitle.length; i++)
  2209. {
  2210. arrTitle.push(elesTitle[i].textContent.trim());
  2211. }
  2212.  
  2213. for (var i = 0; i < elesGame.length; i++)
  2214. {
  2215. var isMatch = false;
  2216. var game = elesGame[i].textContent.trim().toLowerCase();
  2217. for (var j = 0; j < elesTitle.length; j++)
  2218. {
  2219. var title = elesTitle[j].textContent.trim().toLowerCase();
  2220. if (game === title
  2221. || (title.indexOf("|") > -1 && game === title.replace("|", ":"))
  2222. || (game === "ball of light" && title === "ball of light (journey)")
  2223. || (game === "its your last chance in new school" && title === "it is yоur last chance in new schооl")
  2224. || (game === "shake your money simulator 2016" && title === "shake your money simulator")
  2225. || (game === "spakoyno: back to the ussr 2.0" && title === "spakoyno back to the ussr 2.0")
  2226. || (game === "or" && title === "or!"))
  2227. {
  2228. isMatch = true;
  2229.  
  2230. arrTitle = arrTitle.filter(function(value)
  2231. {
  2232. return value !== elesTitle[j].textContent.trim()
  2233. });
  2234. }
  2235.  
  2236. if (isMatch)
  2237. {
  2238. var eleA = document.createElement("a");
  2239. eleA.classList.add("bh_steam");
  2240. eleA.href = elesGame[i].href;
  2241. elesTitle[j].parentElement.parentElement.appendChild(eleA);
  2242.  
  2243. break;
  2244. }
  2245. }
  2246. if (!isMatch)
  2247. {
  2248. console.log("Not match: " + elesGame[i].href + " " + elesGame[i].textContent);
  2249. }
  2250. }
  2251.  
  2252. if (arrTitle.length > 0)
  2253. {
  2254. console.log("Not match: " + arrTitle.length);
  2255. for (var i = 0; i < arrTitle.length; i++)
  2256. {
  2257. console.log("Not match: " + arrTitle[i]);
  2258. }
  2259. }
  2260.  
  2261. markOwned(".wholesale-card > a[href*='store.steampowered.com/']", function(ele)
  2262. {
  2263. return ele.parentElement;
  2264. }, null, "bh_owned");
  2265. }
  2266. }
  2267.  
  2268. } // End onload
  2269. });
  2270. }
  2271. }
  2272. else if (url.indexOf("bunchkeys.com") > -1)
  2273. {
  2274. GM_addStyle(
  2275. " .bh_owned { border: #B5D12E 3px solid !important; "
  2276. + " margin-left: -3px; margin-top: -3px; } "
  2277. );
  2278.  
  2279. // Add mark button
  2280. {
  2281. var divButton = document.createElement("div");
  2282. divButton.classList.add("bh_button");
  2283. divButton.id = "bh_markOwned";
  2284.  
  2285. var eleA = document.createElement("a");
  2286. eleA.setAttribute("onclick", "return false;");
  2287. eleA.textContent = "Mark Owned";
  2288.  
  2289. divButton.appendChild(eleA);
  2290. document.body.appendChild(divButton);
  2291.  
  2292. divButton.addEventListener("click", function()
  2293. {
  2294. markOwned("a[href*='store.steampowered.com/']", function(ele)
  2295. {
  2296. return ele.parentElement;
  2297. }, null, "bh_owned");
  2298. });
  2299. }
  2300. }
  2301. else if (url.indexOf("sgtools.info") > -1)
  2302. {
  2303. GM_addStyle(
  2304. " .bh_owned { background-color: #71A034 !important; } "
  2305. );
  2306. if (url.indexOf("/lastbundled") > -1)
  2307. {
  2308. markOwned("#content > div > table > tbody > tr > td > a[href*='store.steampowered.com/']", function(ele)
  2309. {
  2310. return ele.parentElement.parentElement;
  2311. }, null, "bh_owned");
  2312. }
  2313. else if (url.indexOf("/deals") > -1)
  2314. {
  2315. markOwned(".deal_game_image > img[src*='cdn.akamai.steamstatic.com/steam/']", function(ele)
  2316. {
  2317. return ele.parentElement;
  2318. }, null, "bh_owned");
  2319. }
  2320. else if (url.indexOf("/whitelisted") > -1)
  2321. {
  2322. markOwned(".cmGame > a[href*='store.steampowered.com/']", function(ele)
  2323. {
  2324. return ele.parentElement;
  2325. }, null, "bh_owned");
  2326. }
  2327. }
  2328. else if (url.indexOf("steamkeys.ovh") > -1)
  2329. {
  2330. markOwned("td > a[href*='store.steampowered.com/']", function(ele)
  2331. {
  2332. return ele.parentElement.parentElement;
  2333. }, null, "bh_owned");
  2334. }
  2335. else if (url.indexOf("steamdb.info") > -1)
  2336. {
  2337. if (window !== window.parent)
  2338. {
  2339. return;
  2340. }
  2341.  
  2342. GM_addStyle(
  2343. " .bh_owned, tr.bh_owned td { background-color: #DDF7D3 !important; } "
  2344. + " .bh_owned_transparent { background-color: #bcf0a880 !important; } "
  2345. );
  2346.  
  2347. markOwned(" \
  2348. #apps .app \
  2349. , #dlc .app \
  2350. , .container > .table .app \
  2351. , .sales-section .app \
  2352. , .page-search .app \
  2353. ", null, function(ele)
  2354. {
  2355. return ele.getAttribute("data-appid");
  2356. }, "bh_owned");
  2357.  
  2358. markOwned(" \
  2359. #subs .package \
  2360. , .sales-section .package \
  2361. , .page-search .package \
  2362. ", null, function(ele)
  2363. {
  2364. return "/steam/subs/" + ele.getAttribute("data-subid");
  2365. }, "bh_owned");
  2366.  
  2367. markOwned(".table-products .app"
  2368. , null, function(ele)
  2369. {
  2370. return ele.getAttribute("data-appid");
  2371. }, "bh_owned_transparent");
  2372.  
  2373. markOwned(".app-history .appid"
  2374. , function(ele)
  2375. {
  2376. return ele.parentElement;
  2377. }, function(ele)
  2378. {
  2379. return ele.textContent.trim();
  2380. }, "bh_owned");
  2381. }
  2382.  
  2383. window.addEventListener("beforeunload", function(e)
  2384. {
  2385. clearTimeoutAll();
  2386. clearIntervalAll();
  2387. });
  2388. }
  2389.  
  2390. attachOnReady(main);
  2391.  
  2392. })();
  2393.  
  2394. // End