- // ==UserScript==
- // @name Greasy Fork镜像 User Statistics+
- // @namespace -
- // @version 1.5.1
- // @description shows user statistics as total installs, total scripts etc.
- // @author NotYou
- // @match *://gf.qytechs.cn/*/users/*
- // @match *://sleazyfork.org/*/users/*
- // @license GPL-3.0-or-later
- // @run-at document-end
- // @grant none
- // ==/UserScript==
-
- (function() {
- class Utils {
- static getCurrentTranslationId() {
- const $languageSelector = document.querySelector('.language-selector-locale')
-
- return $languageSelector ? $languageSelector.value : 'en'
- }
-
- static addStyle(css) {
- const selectors = Object.keys(css)
- const style = document.createElement('style')
- let cssFinal = ''
-
- selectors.forEach(selector => {
- const properties = Object.keys(css[selector])
-
- cssFinal += selector + '{'
-
- properties.forEach(property => {
- cssFinal += property.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`) + ':' + css[selector][property] + ';'
- })
-
- cssFinal += '}'
- })
-
- style.textContent = cssFinal
- document.querySelector('head').appendChild(style)
- }
-
- static capitalize(str) {
- return str[0].toUpperCase() + str.slice(1)
- }
- }
-
- class Translation {
- static get data() {
- return {
- 'ar': {
- stats: 'إحصائيات المستخدم',
- works: 'يعمل المستخدم',
- },
- 'bg': {
- stats: 'Потребителска статистика',
- works: 'Потребителят работи',
- },
- 'cs': {
- stats: 'Statistiky uživatelů',
- works: 'Uživatel pracuje',
- },
- 'da': {
- stats: 'Brugerstatistik',
- works: 'Brugeren fungerer',
- },
- 'de': {
- stats: 'Benutzerstatistiken',
- works: 'Benutzer funktioniert',
- },
- 'el': {
- stats: 'Στατιστικά στοιχεία χρηστών',
- works: 'Ο χρήστης λειτουργεί',
- },
- 'en': {
- stats: 'User statistics',
- works: 'User works',
- },
- 'eo': {
- stats: 'Statistiko de uzantoj',
- works: 'Uzanto funkcias',
- },
- 'es': {
- stats: 'Estadísticas de usuario',
- works: 'El usuario trabaja',
- },
- 'fi': {
- stats: 'Käyttäjätilastot',
- works: 'Käyttäjä toimii',
- },
- 'fr': {
- stats: 'Statistiques d\'utilisateurs',
- works: 'L\'utilisateur travaille',
- },
- 'he': {
- stats: 'סטטיסטיקות משתמשים',
- works: 'משתמש עובד',
- },
- 'hu': {
- stats: 'Felhasználói statisztikák',
- works: 'Felhasználó működik',
- },
- 'id': {
- stats: 'Statistik pengguna',
- works: 'Pengguna bekerja',
- },
- 'it': {
- stats: 'Statistiche utente',
- works: 'L\'utente lavora',
- },
- 'ja': {
- stats: 'ユーザー統計',
- works: 'ユーザーは動作します',
- },
- 'ko': {
- stats: '사용자 통계',
- works: '사용자 작품',
- },
- 'ne': {
- stats: 'Gebruikersstatistieken',
- works: 'Gebruiker werkt',
- },
- 'pl': {
- stats: 'Statystyki użytkowników',
- works: 'Użytkownik pracuje',
- },
- 'ro': {
- stats: 'Statistici utilizatori',
- works: 'Utilizatorul lucrează',
- },
- 'ru': {
- stats: 'Статистика пользователей',
- works: 'Пользовательские работы',
- },
- 'tr': {
- stats: 'Kullanıcı istatistikleri',
- works: 'Kullanıcı işleri',
- },
- 'uk': {
- stats: 'Статистика користувачів',
- works: 'Користувач працює',
- },
- 'vi': {
- stats: 'Thống kê người dùng',
- works: 'Người dùng hoạt động',
- },
- 'zh-CN': {
- stats: '用户统计',
- works: '用户作品',
- },
- 'zh-TW': {
- stats: '用戶統計',
- works: '用戶作品',
- },
- }
- }
-
- static getById(id) {
- return this.data[id]
- }
- }
-
- class Data {
- constructor() {
- this.total = 0
- this.daily = 0
- this.scripts = 0
- this.styles = 0
- this.libraries = 0
- this.stats = 0
- this.works = 0
- }
-
- increaseTotal(value) {
- this.total += value
- this.stats += value
- }
-
- increaseDaily(value) {
- this.daily += value
- this.stats += value
- }
-
- increaseScripts() {
- this.scripts++
- this.works++
- }
-
- increaseStyles() {
- this.styles++
- this.works++
- }
-
- increaseLibraries() {
- this.libraries++
- this.works++
- }
- }
-
- class Stats {
- constructor(title, data, maxValue) {
- this.title = Object.assign(document.createElement('h3'), {
- textContent: title
- })
- this.data = data
- this.node = document.createElement('div')
- this.node.appendChild(this.title)
-
- for (const key in data) {
- const value = data[key]
- const percentage = value / maxValue * 100
-
- if (percentage > 0) {
- const $bar = this.bar(percentage, key, value)
-
- this.node.appendChild($bar)
- }
- }
- }
-
- bar(percentage, key, value) {
- const $bar = document.createElement('div')
- const $progress = document.createElement('div')
- const $text = document.createElement('span')
- let bg = '128, 128, 128'
-
- $bar.className = 'statistics-bar'
-
- switch (key) {
- case 'total':
- bg = '255, 28, 28'
- break
- case 'daily':
- bg = '255, 58, 58'
- break
- case 'styles':
- bg = '50, 149, 208'
- break
- case 'scripts':
- bg = '236, 203, 27'
- break
- case 'libraries':
- bg = '221, 102, 15'
- break
- }
- $progress.style.width = percentage + '%'
- $progress.style.backgroundColor = 'rgba(' + bg + ', .7)'
-
- $text.textContent = Utils.capitalize(key) + ` (${value.toLocaleString()})`
-
- $bar.appendChild($text)
- $bar.appendChild($progress)
-
- return $bar
- }
- }
-
- class Styles {
- static init() {
- Utils.addStyle({
- '#user-statistics': {
- position: 'relative',
- },
-
- '.statistics-bar': {
- width: 'calc(100% - 2.4vw)',
- margin: '1em',
- marginBottom: '1.5em',
- },
-
- '.statistics-bar div': {
- height: '3px',
- borderRadius: '20px',
- padding: '3px',
- position: 'relative',
- },
-
- '.statistics-bar div[style*=" 0%"]': {
- padding: '0',
- },
-
- '.statistics-bar div[style*=" 0%"] + span': {
- color: 'unset !important',
- },
-
- '#user-statistics-pin-btn': {
- width: '25px',
- height: '25px',
- backgroundColor: 'rgb(191, 191, 191)',
- display: 'block',
- position: 'absolute',
- right: '10px',
- top: '10px',
- borderRadius: '50%',
- cursor: 'pointer',
- 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=)',
- backgroundSize: '80% 80%',
- backgroundRepeat: 'no-repeat',
- backgroundPosition: '40% 40%',
- filter: 'grayscale(1)',
- },
-
- '#user-statistics.stats-pinned': {
- position: 'fixed',
- zIndex: '9',
- right: '10px',
- top: 'calc(50vh - 175px)',
- borderRadius: '4px',
- width: '400px',
- height: '350px',
- padding: '4px',
- border: '1px solid rgb(0, 0 ,0)',
- },
-
- '#user-statistics.stats-pinned #user-statistics-pin-btn': {
- /* GPL-3.0 @Saki */
- backgroundImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABqUlEQVR42u2USUvDUBSF8zIYEzqXlnbR/gYHdCe4VRyg0ILiwuF3OSxEsYWCInUruFMcfkO7KZ2blrRmIJ4XWsnCgVREhDwINDd957vvnJsQ5pcX8QAegIlEIpymaTP9fv/RWff5fAuiKD43m01jYkA8Hud1XT8MhUJZRVFyELseQddQy7fb7SIge9VqVXcNCAaDPMuyR6lUaofneQLAoNPpZC3LYsLhcCEQCEimaVrlcvmMELLbarUMVwBJkuYSicQdOpUgQCBs9Xo9DT+pPVP2ZixAB7VabRkW3ru2CJ2uRqNR2q1MAWPQ+DlEh7BtC91f/iTkFVxFv98vOgEQ15BBrtFoXE0cMl04wQYAeQpw1pHJEPZkALiZGADhTQDOIS59ZBEyGcCeLK6SawBCXkwmk7c0ZIfgK9VHJu+ngU1qvV5fgmVPrgAQ5tHwSTqd3uY4zh5T2i3dM7JsGmPKVCqVU/xv3/WY0hWLxQTDMI4xTRn4nYOI/aLhfh0NXKBWEAThAGP66dv8bcj0JPhUzKqq+uCsy7I8D/GXbrdrfrX/7z92HuD/A94AoxXXGeB8ZfgAAAAASUVORK5CYII=)',
- },
-
- '#user-statistics.stats-pinned .statistics-bar': {
- margin: '.5em',
- },
-
- '.statistics-bar div::before': {
- content: '""',
- position: 'absolute',
- width: 'calc(100% - 2px)',
- height: '7px',
- margin: '-5px -5px',
- borderRadius: '20px',
- boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.3), 0 0 4px 0 rgba(0, 0, 0, 0.3) inset',
- border: '1px solid rgb(34, 34, 34)',
- padding: '2px',
- },
-
- '#user-statistics-pin-btn:only-child': {
- display: 'none',
- }
- })
- }
- }
-
- class Main {
- static init() {
- const translationId = Utils.getCurrentTranslationId()
- const currentTranslation = Translation.getById(translationId)
- const data = new Data()
-
- const $stats = document.createElement('div')
- $stats.id = 'user-statistics'
-
- const $pinBtn = document.createElement('div')
- $pinBtn.id = 'user-statistics-pin-btn'
- $pinBtn.addEventListener('click', () => {
- const styles = window.getComputedStyle(document.body)
-
- if ($stats.classList.contains('stats-pinned')) {
- $stats.style.cssText = ''
- } else {
- $stats.style.backgroundColor = styles.backgroundColor
- $stats.style.color = styles.color
- }
-
- $stats.classList.toggle('stats-pinned')
- })
-
- $stats.appendChild($pinBtn)
-
- const isCitrusGF = Boolean(document.querySelector('#script-table'))
-
- if (isCitrusGF) {
- document.querySelectorAll('#script-table tbody tr').forEach(script => {
- data.increaseTotal(Number(script.querySelector(':nth-child(5)').textContent))
- data.increaseDaily(Number(script.querySelector(':nth-child(4)').textContent))
- data.increaseScripts()
- })
- } else {
- document.querySelectorAll('.script-list > li').forEach(script => {
- const { dataset } = script
-
- data.increaseTotal(Number(dataset.scriptTotalInstalls))
- data.increaseDaily(Number(dataset.scriptDailyInstalls))
-
- if (dataset.scriptType === 'library') {
- data.increaseLibraries()
- } else if (dataset.scriptLanguage === 'js') {
- data.increaseScripts()
- } else {
- data.increaseStyles()
- }
- })
- }
-
- const stats = new Stats(currentTranslation.stats, {
- total: data.total,
- daily: data.daily
- }, data.stats)
-
- const works = new Stats(currentTranslation.works, {
- scripts: data.scripts,
- styles: data.styles,
- libraries: data.libraries
- }, data.works)
-
- if (data.stats) {
- $stats.appendChild(stats.node)
- }
-
- if (data.works) {
- $stats.appendChild(works.node)
- }
-
- Styles.init()
-
- document.querySelector('#about-user').appendChild($stats)
- }
- }
-
- Main.init()
- })()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-