Greasy Fork 支持简体中文。

Bundle Helper

Add tools for many bundle sites.

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