Steam Community modals as links

Converts pseudo-links from Steam Community into real anchor tags. Instead of opening them as iframes inside modal dialogs, they now open as a full page, they can be opened in another tab, and their URLs can be copied.

  1. // ==UserScript==
  2. // @name Steam Community modals as links
  3. // @namespace https://denilson.sa.nom.br/
  4. // @author Denilson Sá Maia
  5. // @version 1.0
  6. // @description Converts pseudo-links from Steam Community into real anchor tags. Instead of opening them as iframes inside modal dialogs, they now open as a full page, they can be opened in another tab, and their URLs can be copied.
  7. // @match *://steamcommunity.com/*
  8. // @run-at document-end
  9. // @icon https://steamcommunity.com/favicon.ico
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. //////////////////////////////////////////////////
  17. // Convenience functions
  18.  
  19. // Returns a new function that will call the callback without arguments
  20. // after timeout milliseconds of quietness.
  21. function debounce(callback, timeout = 500) {
  22. let id = null;
  23. return function() {
  24. clearTimeout(id);
  25. id = setTimeout(callback, timeout);
  26. };
  27. }
  28.  
  29. const active_mutation_observers = [];
  30.  
  31. // Returns a new MutationObserver that observes a specific node.
  32. // The observer will be immediately active.
  33. function debouncedMutationObserver(rootNode, callback, timeout = 500) {
  34. const func = debounce(callback, timeout);
  35. func();
  36. const observer = new MutationObserver(func);
  37. observer.observe(rootNode, {
  38. subtree: true,
  39. childList: true,
  40. attributes: false,
  41. });
  42. active_mutation_observers.push(observer);
  43. return observer;
  44. }
  45.  
  46. // Adds a MutationObserver to each root node matched by the CSS selector.
  47. function debouncedMutationObserverSelectorAll(rootSelector, callback, timeout = 500) {
  48. for (const root of document.querySelectorAll(rootSelector)) {
  49. debouncedMutationObserver(root, callback, timeout);
  50. }
  51. }
  52.  
  53. function stopAllMutationObservers() {
  54. for (const mo of active_mutation_observers) {
  55. mo.disconnect();
  56. }
  57. active_mutation_observers.length = 0;
  58. }
  59.  
  60. //////////////////////////////////////////////////
  61.  
  62. function main() {
  63. debouncedMutationObserverSelectorAll("#AppHubContent", function() {
  64. for (const card of document.querySelectorAll(".apphub_Card.modalContentLink[data-modal-content-url]")) {
  65. const a = document.createElement("a");
  66. a.href = card.dataset.modalContentUrl;
  67. a.append(...card.childNodes);
  68. card.append(a);
  69. card.classList.remove('modalContentLink');
  70. }
  71. });
  72. }
  73.  
  74. main();
  75.  
  76. })();