Greasy Fork User Statistics+

shows user statistics as total installs, total scripts and etc.

当前为 2022-11-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Greasy Fork User Statistics+
  3. // @namespace -
  4. // @version 1.2.0
  5. // @description shows user statistics as total installs, total scripts and etc.
  6. // @author NotYou
  7. // @include *greasyfork.org/*/users/*
  8. // @include *sleazyfork.org/*/users/*
  9. // @license GPL-3.0-or-later
  10. // @run-at document-end
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. let translations = {
  16. 'ar': {
  17. stats: 'إحصائيات المستخدم',
  18. works: 'يعمل المستخدم',
  19. },
  20. 'bg': {
  21. stats: 'Потребителска статистика',
  22. works: 'Потребителят работи',
  23. },
  24. 'cs': {
  25. stats: 'Statistiky uživatelů',
  26. works: 'Uživatel pracuje',
  27. },
  28. 'da': {
  29. stats: 'Brugerstatistik',
  30. works: 'Brugeren fungerer',
  31. },
  32. 'de': {
  33. stats: 'Benutzerstatistiken',
  34. works: 'Benutzer funktioniert',
  35. },
  36. 'el': {
  37. stats: 'Στατιστικά στοιχεία χρηστών',
  38. works: 'Ο χρήστης λειτουργεί',
  39. },
  40. 'en': {
  41. stats: 'User statistics',
  42. works: 'User works',
  43. },
  44. 'eo': {
  45. stats: 'Statistiko de uzantoj',
  46. works: 'Uzanto funkcias',
  47. },
  48. 'es': {
  49. stats: 'Estadísticas de usuario',
  50. works: 'El usuario trabaja',
  51. },
  52. 'fi': {
  53. stats: 'Käyttäjätilastot',
  54. works: 'Käyttäjä toimii',
  55. },
  56. 'fr': {
  57. stats: 'Statistiques d\'utilisateurs',
  58. works: 'L\'utilisateur travaille',
  59. },
  60. 'he': {
  61. stats: 'סטטיסטיקות משתמשים',
  62. works: 'משתמש עובד',
  63. },
  64. 'hu': {
  65. stats: 'Felhasználói statisztikák',
  66. works: 'Felhasználó működik',
  67. },
  68. 'id': {
  69. stats: 'Statistik pengguna',
  70. works: 'Pengguna bekerja',
  71. },
  72. 'it': {
  73. stats: 'Statistiche utente',
  74. works: 'L\'utente lavora',
  75. },
  76. 'ja': {
  77. stats: 'ユーザー統計',
  78. works: 'ユーザーは動作します',
  79. },
  80. 'ko': {
  81. stats: '사용자 통계',
  82. works: '사용자 작품',
  83. },
  84. 'ne': {
  85. stats: 'Gebruikersstatistieken',
  86. works: 'Gebruiker werkt',
  87. },
  88. 'pl': {
  89. stats: 'Statystyki użytkowników',
  90. works: 'Użytkownik pracuje',
  91. },
  92. 'ro': {
  93. stats: 'Statistici utilizatori',
  94. works: 'Utilizatorul lucrează',
  95. },
  96. 'ru': {
  97. stats: 'Статистика пользователей',
  98. works: 'Пользовательские работы',
  99. },
  100. 'tr': {
  101. stats: 'Kullanıcı istatistikleri',
  102. works: 'Kullanıcı işleri',
  103. },
  104. 'uk': {
  105. stats: 'Статистика користувачів',
  106. works: 'Користувач працює',
  107. },
  108. 'vi': {
  109. stats: 'Thống kê người dùng',
  110. works: 'Người dùng hoạt động',
  111. },
  112. 'zh-CN': {
  113. stats: '用户统计',
  114. works: '用户作品',
  115. },
  116. 'zh-TW': {
  117. stats: '用戶統計',
  118. works: '用戶作品',
  119. },
  120. }
  121.  
  122. let currentTranslation = translations[document.querySelector('#language-selector-locale').value] || translations.en
  123. let i = 0
  124.  
  125. let data = new Proxy({
  126. total: 0,
  127. daily: 0,
  128. scripts: 0,
  129. styles: 0,
  130. libraries: 0,
  131.  
  132. stats: 0,
  133. works: 0,
  134. }, {
  135. set(target, prop, value) {
  136. let t = target
  137.  
  138. t[prop] = value
  139.  
  140. t.stats = t.total + t.daily
  141. t.works = t.scripts + t.styles + t.libraries
  142. }
  143. })
  144.  
  145. let stats = document.createElement('div')
  146.  
  147. stats.id = 'user-statistics'
  148.  
  149. addStyle({
  150. '.statistics-bar': {
  151. width: 'calc(100% - 2.4vw)',
  152. margin: '1em',
  153. },
  154.  
  155. '.statistics-bar div': {
  156. height: '20px',
  157. borderRadius: '4px',
  158. padding: '3px',
  159. },
  160.  
  161. '.statistics-bar span': {
  162. position: 'relative',
  163. bottom: '23px',
  164. zIndex: '50',
  165. height: '0',
  166. display: 'block',
  167. left: '5px',
  168. },
  169.  
  170. '.statistics-bar div[style*=" 0%"]': {
  171. padding: '0',
  172. },
  173.  
  174. '.statistics-bar div[style*=" 0%"] + span': {
  175. color: 'unset !important',
  176. },
  177. })
  178.  
  179. let isCitrusGF = Boolean(document.querySelector('#script-table'))
  180.  
  181. document.querySelectorAll('.script-list > li, #script-table tbody tr').forEach(e => {
  182. let dataset = e.dataset
  183.  
  184. if(isCitrusGF && Object.keys(dataset).length === 0) {
  185. data.daily += +e.querySelector(':nth-child(4)').textContent
  186. data.total += +e.querySelector(':nth-child(5)').textContent
  187.  
  188. data.scripts++
  189. } else {
  190. data.total += +dataset.scriptTotalInstalls
  191. data.daily += +dataset.scriptDailyInstalls
  192.  
  193. if(dataset.scriptType === 'library') {
  194. data.libraries++
  195. } else {
  196. data[dataset.scriptLanguage === 'js' ? 'scripts' : 'styles']++
  197. }
  198. }
  199. })
  200.  
  201. createStat({
  202. title: 'stats',
  203. values: [
  204. 'total',
  205. 'daily',
  206. ]
  207. })
  208.  
  209. createStat({
  210. title: 'works',
  211. values: [
  212. 'scripts',
  213. 'styles',
  214. 'libraries',
  215. ]
  216. })
  217.  
  218. function createStat(input) {
  219. let { values, title } = input
  220. let titleEl = document.createElement('h3')
  221.  
  222. if(data[title] > 0) {
  223. titleEl.textContent = currentTranslation[title]
  224. stats.appendChild(titleEl)
  225.  
  226. values.forEach(e => {
  227. let value = data[e]
  228. let total = data[title]
  229. let width = value / total * 100
  230.  
  231. if(width > 0) {
  232. createBar(width, e, value)
  233. }
  234. })
  235. }
  236.  
  237. i++
  238. }
  239.  
  240. function createBar(width, name, value) {
  241. let bar = document.createElement('div')
  242. let barActual = document.createElement('div')
  243. let text = document.createElement('span')
  244. let bg = '128, 128, 128'
  245.  
  246. bar.className = 'statistics-bar'
  247.  
  248. switch (name) {
  249. case 'total':
  250. bg = '255, 28, 28'
  251. break
  252. case 'daily':
  253. bg = '255, 58, 58'
  254. break
  255. case 'styles':
  256. bg = '50, 149, 208'
  257. break
  258. case 'scripts':
  259. bg = '236, 203, 27'
  260. break
  261. case 'libraries':
  262. bg = '221, 102, 15'
  263. break
  264. }
  265. barActual.style.width = width + '%'
  266. barActual.style.backgroundColor = 'rgba(' + bg + ', .7)'
  267.  
  268. if(i > 0) {
  269. text.style.color = 'rgb(0, 0, 0)'
  270. }
  271.  
  272. text.textContent = capitalize(name) + ` (${value.toLocaleString()})`
  273.  
  274. bar.appendChild(barActual)
  275. bar.appendChild(text)
  276. stats.appendChild(bar)
  277. }
  278.  
  279. function addStyle(css) {
  280. let keys = Object.keys(css)
  281. let cssActual = ''
  282. let style = document.createElement('style')
  283.  
  284. keys.forEach(e => {
  285. let _keys = Object.keys(css[e])
  286.  
  287. cssActual += e + '{'
  288.  
  289. _keys.forEach(r => {
  290. cssActual += r.replace(/[A-Z]/, m => `-${m.toLowerCase()}`) + ':' + css[e][r] + ';'
  291. })
  292.  
  293. cssActual += '}'
  294. })
  295.  
  296. style.textContent = cssActual
  297. document.querySelector('head').appendChild(style)
  298. }
  299.  
  300. function capitalize(str) {
  301. return str[0].toUpperCase() + str.slice(1)
  302. }
  303.  
  304. document.querySelector('#about-user').appendChild(stats)
  305. })()
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.  
  317.  
  318.  
  319.  
  320.