Script Finder

Script Finder allows you to find userscripts from greasyfork on any website.

当前为 2023-07-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Script Finder
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.0
  5. // @description Script Finder allows you to find userscripts from greasyfork on any website.
  6. // @author shiquda
  7. // @namespace https://github.com/shiquda/shiquda_UserScript
  8. // @supportURL https://github.com/shiquda/shiquda_UserScript/issues
  9. // @match *://*/*
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM_addStyle
  12. // @license AGPL-3.0
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. const domainParts = window.location.hostname.split('.').slice(-2);
  17. const domain = domainParts.join('.');
  18. const errorMessage = "Failed to retrieve script information or there are no available scripts for this domain.";
  19.  
  20. function getScriptsInfo(domain) {
  21.  
  22. var url = `https://greasyfork.org/scripts/by-site/${domain}`;
  23. GM_xmlhttpRequest({
  24. method: "GET",
  25. url: url,
  26. onload: (response) => {
  27.  
  28. const parser = new DOMParser();
  29. const doc = parser.parseFromString(response.responseText, "text/html");
  30. const scripts = doc.querySelector("#browse-script-list")?.querySelectorAll('[data-script-id]');
  31. let scriptsInfo = [];
  32. if (!scripts) {
  33. scriptsInfo = errorMessage
  34. } else {
  35. for (var i = 0; i < scripts.length; i++) {
  36. scriptsInfo.push(parseScriptInfo(scripts[i]));
  37. }
  38. }
  39. console.log(scriptsInfo);
  40. showScriptsInfo(scriptsInfo); // 显示脚本信息
  41. },
  42. onerror: () => {
  43. console.log("Some error!");
  44. const scriptsInfo = errorMessage
  45. showScriptsInfo(scriptsInfo)
  46. }
  47. });
  48. }
  49.  
  50. // 解析脚本信息
  51. function parseScriptInfo(script) {
  52. return {
  53. id: script.getAttribute('data-script-id'),
  54. name: script.getAttribute('data-script-name'),
  55. author: script.querySelector("dd.script-list-author").textContent,
  56. description: script.querySelector(".script-description").textContent,
  57. // version: script.getAttribute('data-script-version'),
  58. url: 'https://greasyfork.org/scripts/' + script.getAttribute('data-script-id'),
  59. // createDate: script.getAttribute('data-script-created-date'),
  60. // updateDate: script.getAttribute('data-script-updated-date'),
  61. installs: script.getAttribute('data-script-total-installs'),
  62. // dailyInstalls: script.getAttribute('data-script-daily-installs'),
  63. ratingScore: script.getAttribute('data-script-rating-score')
  64. };
  65. }
  66.  
  67. function showScriptsInfo(scriptsInfo) {
  68. GM_addStyle(`
  69. button.script-button {
  70. position: fixed;
  71. bottom: 50%;
  72. right: -50px;
  73. transform: translateY(50%);
  74. padding: 12px;
  75. font-size: 16px;
  76. border: none;
  77. border-radius: 4px;
  78. background-color: #1e90ff;
  79. color: #ffffff;
  80. cursor: pointer;
  81. transition: right 0.3s;
  82. }
  83. div.info-container {
  84. display: none;
  85. position: fixed;
  86. top: 10%;
  87. right: 100px;
  88. width: 600px;
  89. padding: 12px;
  90. background-color: #ffffff;
  91. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
  92. border-radius: 4px;
  93. opacity: 0;
  94. transition: opacity 0.3s;
  95. z-index: 9999;
  96. max-height: 80vh;
  97. overflow-y: auto;
  98. }
  99. ul.info-list {
  100. list-style: none;
  101. margin: 0;
  102. padding: 0;
  103. }
  104.  
  105. li.info-item {
  106. margin-bottom: 10px;
  107. border: 1px solid #ddd;
  108. padding: 10px;
  109. display: flex;
  110. flex-direction: column;
  111. }
  112.  
  113. .div.script-container {
  114. display: flex;
  115. flex-direction: column;
  116. }
  117.  
  118. a.script-link {
  119. font-size: 18px;
  120. font-weight: bold;
  121. margin-bottom: 5px;
  122. }
  123.  
  124. p.script-description {
  125. margin-top: 2px;
  126. margin-bottom: 5px;
  127. }
  128.  
  129. div.details-container {
  130. font-size: 18px;
  131. font-weight: bold;
  132. display: flex;
  133. justify-content: space-between;
  134. }
  135.  
  136. span.script-details {
  137. flex-grow: 1;
  138. text-align: left;
  139. }
  140. div.table-header {
  141. font-size: 25px;
  142. }
  143.  
  144. input.script-search-input {
  145. width: 96%;
  146. padding: 10px;
  147. font-size: 18px;
  148. border: 1px solid #ddd;
  149. border-radius: 4px;
  150. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
  151. margin-bottom: 15px;
  152. margin-top: 20px;
  153. }
  154. `);
  155.  
  156.  
  157. // 创建按钮
  158. var button = document.createElement('button');
  159. button.className = 'script-button';
  160. button.innerText = 'Scripts';
  161.  
  162. // 创建脚本容器
  163. var infoContainer = document.createElement('div');
  164. infoContainer.className = 'info-container';
  165.  
  166. // 创建搜索框
  167. var searchInput = document.createElement('input');
  168. searchInput.type = 'text';
  169. searchInput.placeholder = 'Search scripts...';
  170. searchInput.className = 'script-search-input';
  171.  
  172. // 创建表头
  173. var tableHeader = document.createElement('div');
  174. // tableHeader.style.display = 'flex';
  175. // tableHeader.style.justifyContent = 'space-between';
  176. tableHeader.className = 'table-header';
  177. tableHeader.appendChild(document.createTextNode('Script Finder'));
  178. tableHeader.appendChild(searchInput);
  179.  
  180. // 创建脚本列表
  181. var infoList = document.createElement('ul');
  182. infoList.className = 'info-list';
  183.  
  184. // 插入脚本
  185. if (scriptsInfo === errorMessage) {
  186. infoList.innerHTML = errorMessage;
  187. } else {
  188. for (var i = 0; i < scriptsInfo.length; i++) {
  189. var script = scriptsInfo[i];
  190. var listItem = document.createElement('li');
  191. listItem.className = 'info-item';
  192.  
  193. var scriptContainer = document.createElement('div');
  194. scriptContainer.className = 'script-container';
  195.  
  196. var nameElement = document.createElement('a');
  197. nameElement.className = 'script-link';
  198. nameElement.innerText = script.name;
  199. nameElement.href = script.url;
  200. nameElement.target = '_blank';
  201.  
  202. var descriptionElement = document.createElement('p');
  203. descriptionElement.className = 'script-description';
  204. descriptionElement.innerHTML = script.description;
  205.  
  206. var detailsContainer = document.createElement('div');
  207. detailsContainer.className = 'details-container';
  208.  
  209. var authorElement = document.createElement('span');
  210. authorElement.className = 'script-details';
  211. authorElement.innerText = 'Author: ' + script.author;
  212.  
  213. var installsElement = document.createElement('span');
  214. installsElement.className = 'script-details';
  215. installsElement.innerText = 'Installs: ' + script.installs;
  216.  
  217. var ratingElement = document.createElement('span');
  218. ratingElement.className = 'script-details';
  219. ratingElement.innerText = 'Rating: ' + script.ratingScore;
  220.  
  221. detailsContainer.appendChild(authorElement);
  222. detailsContainer.appendChild(installsElement);
  223. detailsContainer.appendChild(ratingElement);
  224.  
  225. scriptContainer.appendChild(nameElement);
  226. scriptContainer.appendChild(descriptionElement);
  227. scriptContainer.appendChild(detailsContainer);
  228.  
  229. listItem.appendChild(scriptContainer);
  230. infoList.appendChild(listItem);
  231. }
  232. }
  233.  
  234. infoContainer.appendChild(tableHeader)
  235. infoContainer.appendChild(infoList);
  236.  
  237. var timeout;
  238. button.addEventListener('mouseenter', function () {
  239. clearTimeout(timeout);
  240. button.style.right = '10px';
  241. });
  242.  
  243. button.addEventListener('mouseleave', function () {
  244. timeout = setTimeout(function () {
  245. button.style.right = '-50px';
  246. }, 500);
  247. });
  248.  
  249. button.addEventListener('click', function (event) {
  250. event.stopPropagation();
  251. button.style.right = '10px';
  252. infoContainer.style.display = "block"
  253. infoContainer.style.opacity = 1
  254. });
  255.  
  256.  
  257.  
  258. infoContainer.addEventListener('click', function (event) {
  259. event.stopPropagation();
  260. });
  261.  
  262. searchInput.addEventListener('input', () => {
  263. searchScript()
  264. })
  265.  
  266. document.body.addEventListener('click', function () {
  267. clearTimeout(timeout);
  268. button.style.right = '-50px';
  269. infoContainer.style.opacity = 0
  270. });
  271.  
  272. document.body.appendChild(button);
  273.  
  274. document.body.appendChild(infoContainer);
  275. }
  276.  
  277. function searchScript() {
  278. const searchWord = document.querySelector('.script-search-input').value;
  279. const scriptList = document.querySelectorAll('.info-item')
  280. for (let i = 0; i < scriptList.length; i++) {
  281. if (scriptList[i].innerText.includes(searchWord)) {
  282. scriptList[i].style.display = 'block'
  283. } else {
  284. scriptList[i].style.display = 'none'
  285. }
  286. }
  287. }
  288.  
  289. getScriptsInfo(domain);
  290. })();
  291.  
  292.  
  293.