Greasy Fork镜像 User Statistics+

shows user statistics as total installs, total scripts etc.

  1. // ==UserScript==
  2. // @name Greasy Fork镜像 User Statistics+
  3. // @namespace -
  4. // @version 1.5.1
  5. // @description shows user statistics as total installs, total scripts etc.
  6. // @author NotYou
  7. // @match *://gf.qytechs.cn/*/users/*
  8. // @match *://sleazyfork.org/*/users/*
  9. // @license GPL-3.0-or-later
  10. // @run-at document-end
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. class Utils {
  16. static getCurrentTranslationId() {
  17. const $languageSelector = document.querySelector('.language-selector-locale')
  18.  
  19. return $languageSelector ? $languageSelector.value : 'en'
  20. }
  21.  
  22. static addStyle(css) {
  23. const selectors = Object.keys(css)
  24. const style = document.createElement('style')
  25. let cssFinal = ''
  26.  
  27. selectors.forEach(selector => {
  28. const properties = Object.keys(css[selector])
  29.  
  30. cssFinal += selector + '{'
  31.  
  32. properties.forEach(property => {
  33. cssFinal += property.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`) + ':' + css[selector][property] + ';'
  34. })
  35.  
  36. cssFinal += '}'
  37. })
  38.  
  39. style.textContent = cssFinal
  40. document.querySelector('head').appendChild(style)
  41. }
  42.  
  43. static capitalize(str) {
  44. return str[0].toUpperCase() + str.slice(1)
  45. }
  46. }
  47.  
  48. class Translation {
  49. static get data() {
  50. return {
  51. 'ar': {
  52. stats: 'إحصائيات المستخدم',
  53. works: 'يعمل المستخدم',
  54. },
  55. 'bg': {
  56. stats: 'Потребителска статистика',
  57. works: 'Потребителят работи',
  58. },
  59. 'cs': {
  60. stats: 'Statistiky uživatelů',
  61. works: 'Uživatel pracuje',
  62. },
  63. 'da': {
  64. stats: 'Brugerstatistik',
  65. works: 'Brugeren fungerer',
  66. },
  67. 'de': {
  68. stats: 'Benutzerstatistiken',
  69. works: 'Benutzer funktioniert',
  70. },
  71. 'el': {
  72. stats: 'Στατιστικά στοιχεία χρηστών',
  73. works: 'Ο χρήστης λειτουργεί',
  74. },
  75. 'en': {
  76. stats: 'User statistics',
  77. works: 'User works',
  78. },
  79. 'eo': {
  80. stats: 'Statistiko de uzantoj',
  81. works: 'Uzanto funkcias',
  82. },
  83. 'es': {
  84. stats: 'Estadísticas de usuario',
  85. works: 'El usuario trabaja',
  86. },
  87. 'fi': {
  88. stats: 'Käyttäjätilastot',
  89. works: 'Käyttäjä toimii',
  90. },
  91. 'fr': {
  92. stats: 'Statistiques d\'utilisateurs',
  93. works: 'L\'utilisateur travaille',
  94. },
  95. 'he': {
  96. stats: 'סטטיסטיקות משתמשים',
  97. works: 'משתמש עובד',
  98. },
  99. 'hu': {
  100. stats: 'Felhasználói statisztikák',
  101. works: 'Felhasználó működik',
  102. },
  103. 'id': {
  104. stats: 'Statistik pengguna',
  105. works: 'Pengguna bekerja',
  106. },
  107. 'it': {
  108. stats: 'Statistiche utente',
  109. works: 'L\'utente lavora',
  110. },
  111. 'ja': {
  112. stats: 'ユーザー統計',
  113. works: 'ユーザーは動作します',
  114. },
  115. 'ko': {
  116. stats: '사용자 통계',
  117. works: '사용자 작품',
  118. },
  119. 'ne': {
  120. stats: 'Gebruikersstatistieken',
  121. works: 'Gebruiker werkt',
  122. },
  123. 'pl': {
  124. stats: 'Statystyki użytkowników',
  125. works: 'Użytkownik pracuje',
  126. },
  127. 'ro': {
  128. stats: 'Statistici utilizatori',
  129. works: 'Utilizatorul lucrează',
  130. },
  131. 'ru': {
  132. stats: 'Статистика пользователей',
  133. works: 'Пользовательские работы',
  134. },
  135. 'tr': {
  136. stats: 'Kullanıcı istatistikleri',
  137. works: 'Kullanıcı işleri',
  138. },
  139. 'uk': {
  140. stats: 'Статистика користувачів',
  141. works: 'Користувач працює',
  142. },
  143. 'vi': {
  144. stats: 'Thống kê người dùng',
  145. works: 'Người dùng hoạt động',
  146. },
  147. 'zh-CN': {
  148. stats: '用户统计',
  149. works: '用户作品',
  150. },
  151. 'zh-TW': {
  152. stats: '用戶統計',
  153. works: '用戶作品',
  154. },
  155. }
  156. }
  157.  
  158. static getById(id) {
  159. return this.data[id]
  160. }
  161. }
  162.  
  163. class Data {
  164. constructor() {
  165. this.total = 0
  166. this.daily = 0
  167. this.scripts = 0
  168. this.styles = 0
  169. this.libraries = 0
  170. this.stats = 0
  171. this.works = 0
  172. }
  173.  
  174. increaseTotal(value) {
  175. this.total += value
  176. this.stats += value
  177. }
  178.  
  179. increaseDaily(value) {
  180. this.daily += value
  181. this.stats += value
  182. }
  183.  
  184. increaseScripts() {
  185. this.scripts++
  186. this.works++
  187. }
  188.  
  189. increaseStyles() {
  190. this.styles++
  191. this.works++
  192. }
  193.  
  194. increaseLibraries() {
  195. this.libraries++
  196. this.works++
  197. }
  198. }
  199.  
  200. class Stats {
  201. constructor(title, data, maxValue) {
  202. this.title = Object.assign(document.createElement('h3'), {
  203. textContent: title
  204. })
  205. this.data = data
  206. this.node = document.createElement('div')
  207. this.node.appendChild(this.title)
  208.  
  209. for (const key in data) {
  210. const value = data[key]
  211. const percentage = value / maxValue * 100
  212.  
  213. if (percentage > 0) {
  214. const $bar = this.bar(percentage, key, value)
  215.  
  216. this.node.appendChild($bar)
  217. }
  218. }
  219. }
  220.  
  221. bar(percentage, key, value) {
  222. const $bar = document.createElement('div')
  223. const $progress = document.createElement('div')
  224. const $text = document.createElement('span')
  225. let bg = '128, 128, 128'
  226.  
  227. $bar.className = 'statistics-bar'
  228.  
  229. switch (key) {
  230. case 'total':
  231. bg = '255, 28, 28'
  232. break
  233. case 'daily':
  234. bg = '255, 58, 58'
  235. break
  236. case 'styles':
  237. bg = '50, 149, 208'
  238. break
  239. case 'scripts':
  240. bg = '236, 203, 27'
  241. break
  242. case 'libraries':
  243. bg = '221, 102, 15'
  244. break
  245. }
  246. $progress.style.width = percentage + '%'
  247. $progress.style.backgroundColor = 'rgba(' + bg + ', .7)'
  248.  
  249. $text.textContent = Utils.capitalize(key) + ` (${value.toLocaleString()})`
  250.  
  251. $bar.appendChild($text)
  252. $bar.appendChild($progress)
  253.  
  254. return $bar
  255. }
  256. }
  257.  
  258. class Styles {
  259. static init() {
  260. Utils.addStyle({
  261. '#user-statistics': {
  262. position: 'relative',
  263. },
  264.  
  265. '.statistics-bar': {
  266. width: 'calc(100% - 2.4vw)',
  267. margin: '1em',
  268. marginBottom: '1.5em',
  269. },
  270.  
  271. '.statistics-bar div': {
  272. height: '3px',
  273. borderRadius: '20px',
  274. padding: '3px',
  275. position: 'relative',
  276. },
  277.  
  278. '.statistics-bar div[style*=" 0%"]': {
  279. padding: '0',
  280. },
  281.  
  282. '.statistics-bar div[style*=" 0%"] + span': {
  283. color: 'unset !important',
  284. },
  285.  
  286. '#user-statistics-pin-btn': {
  287. width: '25px',
  288. height: '25px',
  289. backgroundColor: 'rgb(191, 191, 191)',
  290. display: 'block',
  291. position: 'absolute',
  292. right: '10px',
  293. top: '10px',
  294. borderRadius: '50%',
  295. cursor: 'pointer',
  296. backgroundImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAACDElEQVR42mNkoDFgJFejk6igUIWGfKrb4QudNLGgWE3Ou0RNbvPlj1+qgZa0U92C+SaaBe7iQn0g9pVPX3FaQpYFDVqKNmmKUtsYGRl5Qfz/QHDp45cajyMX2yi2AGR4upL0NqCZPEALGGEWvPn2g+HE2/fVaZfutZNtAczlQCYPsjjI8Lffv4PZt7/9QLGEaAvQXQ5yNUg/1PD/yL5BtoQoC4CGWwMN3w5k8oIMgFkANJzx3Y8fDDAxmAUg5q2v38uAlvRgtaBdR9nVSpg/XICVRQLozO9i7Kzu+IIFCbzf++Zj5d1vP+9c//Lt1IVP375gtcBBVEB0vonWXg5mJl1k1yFFKCN6sIAMn/f4tevip6/PERXJMEvYmRh1iHH5vMevgIa/OYcugTcO0HzCgByhSHHxDpfhREVynreHXqEo9/nPDx8wIUcovmAh2gI7ByfJ6vrGfc/v3VYTWTX3H8+nDyzEupygBTDDHz17qnb/0YOLV1cujcpi/7OKjRESJy9+/l4Wef52DKEQYCTG8JXz57vcvXXznSk/t2iLhhww4pl0X/z8tTbi3O0Qki3AZThMHmbJw28/D6ZdvpdLkgWEDEe2xFiAR3PGw5eHSLJg3ZbtG99+/OiDz3BSAdwCY1Mz/oyCorvXblzbtWnlyhxqGI5igZyCAg8w/f17fP/+N2oYjDWIaAFobgEA6ol7KD65m7AAAAAASUVORK5CYII=)',
  297. backgroundSize: '80% 80%',
  298. backgroundRepeat: 'no-repeat',
  299. backgroundPosition: '40% 40%',
  300. filter: 'grayscale(1)',
  301. },
  302.  
  303. '#user-statistics.stats-pinned': {
  304. position: 'fixed',
  305. zIndex: '9',
  306. right: '10px',
  307. top: 'calc(50vh - 175px)',
  308. borderRadius: '4px',
  309. width: '400px',
  310. height: '350px',
  311. padding: '4px',
  312. border: '1px solid rgb(0, 0 ,0)',
  313. },
  314.  
  315. '#user-statistics.stats-pinned #user-statistics-pin-btn': {
  316. /* GPL-3.0 @Saki */
  317. backgroundImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABqUlEQVR42u2USUvDUBSF8zIYEzqXlnbR/gYHdCe4VRyg0ILiwuF3OSxEsYWCInUruFMcfkO7KZ2blrRmIJ4XWsnCgVREhDwINDd957vvnJsQ5pcX8QAegIlEIpymaTP9fv/RWff5fAuiKD43m01jYkA8Hud1XT8MhUJZRVFyELseQddQy7fb7SIge9VqVXcNCAaDPMuyR6lUaofneQLAoNPpZC3LYsLhcCEQCEimaVrlcvmMELLbarUMVwBJkuYSicQdOpUgQCBs9Xo9DT+pPVP2ZixAB7VabRkW3ru2CJ2uRqNR2q1MAWPQ+DlEh7BtC91f/iTkFVxFv98vOgEQ15BBrtFoXE0cMl04wQYAeQpw1pHJEPZkALiZGADhTQDOIS59ZBEyGcCeLK6SawBCXkwmk7c0ZIfgK9VHJu+ngU1qvV5fgmVPrgAQ5tHwSTqd3uY4zh5T2i3dM7JsGmPKVCqVU/xv3/WY0hWLxQTDMI4xTRn4nYOI/aLhfh0NXKBWEAThAGP66dv8bcj0JPhUzKqq+uCsy7I8D/GXbrdrfrX/7z92HuD/A94AoxXXGeB8ZfgAAAAASUVORK5CYII=)',
  318. },
  319.  
  320. '#user-statistics.stats-pinned .statistics-bar': {
  321. margin: '.5em',
  322. },
  323.  
  324. '.statistics-bar div::before': {
  325. content: '""',
  326. position: 'absolute',
  327. width: 'calc(100% - 2px)',
  328. height: '7px',
  329. margin: '-5px -5px',
  330. borderRadius: '20px',
  331. boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.3), 0 0 4px 0 rgba(0, 0, 0, 0.3) inset',
  332. border: '1px solid rgb(34, 34, 34)',
  333. padding: '2px',
  334. },
  335.  
  336. '#user-statistics-pin-btn:only-child': {
  337. display: 'none',
  338. }
  339. })
  340. }
  341. }
  342.  
  343. class Main {
  344. static init() {
  345. const translationId = Utils.getCurrentTranslationId()
  346. const currentTranslation = Translation.getById(translationId)
  347. const data = new Data()
  348.  
  349. const $stats = document.createElement('div')
  350. $stats.id = 'user-statistics'
  351.  
  352. const $pinBtn = document.createElement('div')
  353. $pinBtn.id = 'user-statistics-pin-btn'
  354. $pinBtn.addEventListener('click', () => {
  355. const styles = window.getComputedStyle(document.body)
  356.  
  357. if ($stats.classList.contains('stats-pinned')) {
  358. $stats.style.cssText = ''
  359. } else {
  360. $stats.style.backgroundColor = styles.backgroundColor
  361. $stats.style.color = styles.color
  362. }
  363.  
  364. $stats.classList.toggle('stats-pinned')
  365. })
  366.  
  367. $stats.appendChild($pinBtn)
  368.  
  369. const isCitrusGF = Boolean(document.querySelector('#script-table'))
  370.  
  371. if (isCitrusGF) {
  372. document.querySelectorAll('#script-table tbody tr').forEach(script => {
  373. data.increaseTotal(Number(script.querySelector(':nth-child(5)').textContent))
  374. data.increaseDaily(Number(script.querySelector(':nth-child(4)').textContent))
  375. data.increaseScripts()
  376. })
  377. } else {
  378. document.querySelectorAll('.script-list > li').forEach(script => {
  379. const { dataset } = script
  380.  
  381. data.increaseTotal(Number(dataset.scriptTotalInstalls))
  382. data.increaseDaily(Number(dataset.scriptDailyInstalls))
  383.  
  384. if (dataset.scriptType === 'library') {
  385. data.increaseLibraries()
  386. } else if (dataset.scriptLanguage === 'js') {
  387. data.increaseScripts()
  388. } else {
  389. data.increaseStyles()
  390. }
  391. })
  392. }
  393.  
  394. const stats = new Stats(currentTranslation.stats, {
  395. total: data.total,
  396. daily: data.daily
  397. }, data.stats)
  398.  
  399. const works = new Stats(currentTranslation.works, {
  400. scripts: data.scripts,
  401. styles: data.styles,
  402. libraries: data.libraries
  403. }, data.works)
  404.  
  405. if (data.stats) {
  406. $stats.appendChild(stats.node)
  407. }
  408.  
  409. if (data.works) {
  410. $stats.appendChild(works.node)
  411. }
  412.  
  413. Styles.init()
  414.  
  415. document.querySelector('#about-user').appendChild($stats)
  416. }
  417. }
  418.  
  419. Main.init()
  420. })()
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433.  
  434.  
  435.  

QingJ © 2025

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