curseforge show all file versions

show all the versions listed for a file, rather than hiding them behind a +2 thing you need to hover over

  1. // ==UserScript==
  2. // @name curseforge show all file versions
  3. // @namespace https://github.com/adrianmgg
  4. // @version 1.1.0
  5. // @description show all the versions listed for a file, rather than hiding them behind a +2 thing you need to hover over
  6. // @author amgg
  7. // @match https://www.curseforge.com/minecraft/*/*/files
  8. // @match https://www.curseforge.com/minecraft/*/*/files/*
  9. // @match https://legacy.curseforge.com/minecraft/*/*/files
  10. // @match https://legacy.curseforge.com/minecraft/*/*/files/*
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=curseforge.com
  12. // @grant unsafeWindow
  13. // @run-at document-start
  14. // @license MIT
  15. // @compatible chrome
  16. // @compatible firefox
  17. // ==/UserScript==
  18.  
  19.  
  20. function main_legacy() {
  21. // the list of other versions is present in the initial html (in the title of an
  22. // element), but they remove that after they set up the +2 hover thing, so if we
  23. // want to get that info we need to get to those elements right away before they
  24. // have a chance to modify it.
  25.  
  26.  
  27. // replaces the .extra-versions placeholder with a list versions, changes styles
  28. // to make it display nicely
  29. function modifyExtraVersionsElem(elem) {
  30. // in case we happen to get the same one multiple times
  31. if(elem.parentElement === null) return;
  32. for(const version of elem.title.split('<br />')) {
  33. const div = document.createElement('div');
  34. div.classList.add('mr-2');
  35. div.textContent = version;
  36. elem.parentElement.appendChild(div);
  37. }
  38. elem.parentElement.classList.add('flex-col');
  39. elem.parentElement.parentElement.style.paddingTop = '2.5px';
  40. elem.parentElement.parentElement.style.paddingBottom = '2.5px';
  41. elem.parentElement.parentElement.style.verticalAlign = 'bottom';
  42. elem.parentElement.removeChild(elem);
  43. }
  44.  
  45. const observer = new MutationObserver((mutations, observer) => {
  46. for(const mutation of mutations) {
  47. if(mutation.type === 'childList') {
  48. for(const node of mutation.addedNodes) {
  49. if(node.nodeType === Node.ELEMENT_NODE) {
  50. if(node.classList.contains('extra-versions')) {
  51. modifyExtraVersionsElem(node);
  52. }
  53. node.querySelectorAll('.extra-versions').forEach(modifyExtraVersionsElem);
  54. }
  55. }
  56. }
  57. }
  58. });
  59.  
  60. observer.observe(document, { childList: true, subtree: true });
  61.  
  62. // once the page is finished loading we don't need to be watching for new nodes,
  63. // so we disconnect the observer
  64. window.addEventListener('DOMContentLoaded', (e) => {
  65. observer.disconnect();
  66. });
  67. }
  68.  
  69. function main_new() {
  70. // not fully working yet
  71.  
  72. function handleModsList(modsList) {
  73. console.log('got mods list!', modsList);
  74. // debugger;
  75. }
  76.  
  77. const XMLHttpRequest_prototype_open = unsafeWindow.XMLHttpRequest.prototype.open;
  78. unsafeWindow.XMLHttpRequest.prototype.open = function(method, url) {
  79. if(/^https:\/\/www\.curseforge\.com\/api\/v1\/mods\/\d+\/files(?:\?.*|$)/.test(url)) {
  80. this.addEventListener('load', (e) => {
  81. handleModsList(JSON.parse(e.target.responseText).data);
  82. });
  83. }
  84. return XMLHttpRequest_prototype_open.apply(this, arguments);
  85. }
  86.  
  87. // TODO just mutation observing the table isn't enough, gonna need to do something a bit fancier. maybe reuse that thing observer chaining thing i wrote for that cohost userscript?
  88. document.addEventListener('DOMContentLoaded', () => {
  89. new MutationObserver((mutations, observer) => {
  90. for(const mutation of mutations) {
  91. for(const node of mutation.addedNodes) {
  92. // TODO
  93. }
  94. }
  95. }).observe(document.querySelector('main > div.project-page > section > div.files-table-container > div.files-table'), {subtree: false, childList: true});
  96. });
  97. }
  98.  
  99. (function() {
  100. if(window.location.hostname === 'legacy.curseforge.com') {
  101. main_legacy();
  102. } else {
  103. main_new();
  104. }
  105. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址