GreasyFork: 导航栏增强

在导航栏上添加用户列表,控制台,收藏等..

目前为 2024-08-13 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GreasyFork: User Control Panel Button
  3. // @name:en GreasyFork: User Control Panel Button
  4. // @name:zh-CN GreasyFork: 导航栏增强
  5. // @name:zh-TW GreasyFork: 導航欄增強
  6. // @name:ko GreasyFork: 네비게이션 바 향상
  7. // @name:ja GreasyFork: ナビゲーションバー強化
  8. // @namespace https://github.com/10086100886
  9. // @match https://greasyfork.org/*
  10. // @match https://sleazyfork.org/*
  11. // @grant none
  12. // @version 0.3.1.21
  13. // @license MIT
  14. // @author CY Fung & 人民的勤务员 <toniaiwanowskiskr47@gmail.com>
  15. // @description To add User Control Panel Button into navigation bar
  16. // @description:en To add User Control Panel Button into navigation bar
  17. // @description:zh-CN 在导航栏上添加用户列表,控制台,收藏等..
  18. // @description:ko 네비게이션 바에 사용자 목록, 콘솔, 즐겨찾기 등을 추가...
  19. // @description:ja ナビゲーションバーにユーザーリスト、コンソール、お気に入りなどを追加...
  20. // @description:zh-TW 在導航欄上添加使用者列表、控制台、收藏等...
  21. // @icon https://greasyfork.org/vite/assets/blacklogo96-CxYTSM_T.png
  22. // @supportURL https://github.com/ChinaGodMan/UserScripts/issues
  23. // @homepageURL https://github.com/ChinaGodMan/UserScripts
  24. // ==/UserScript==
  25.  
  26. (async () => {
  27. let sections = [
  28. { id: '#user-script-sets-section' },
  29. { id: '#control-panel' },
  30. // { id: '#user-library-list-section', name: '库' },
  31. //{ id: '#user-unlisted-script-list-section', name: '没上架' },
  32. // { id: '#user-discussions', name: '讨论' },
  33. { id: '#user-script-list-section' }
  34. ]
  35.  
  36. function preSetup() {
  37. let pos = document.querySelectorAll('#site-nav>nav>li.with-submenu')
  38. pos = pos.length >= 1 ? pos[pos.length - 1] : null
  39.  
  40. if (!pos) return
  41.  
  42. pos.parentNode.style.minHeight = '2.8rem'
  43.  
  44. return { pos }
  45. }
  46.  
  47. function setup(m, namespace) {
  48. const { cpmRoot } = m
  49.  
  50. let h = cpmRoot.querySelector('h3') || cpmRoot.querySelector('header')
  51. if (!h) return
  52.  
  53. let nav = document.createElement('nav')
  54.  
  55. let addedText = new Set()
  56. let lastText = null
  57.  
  58. for (const anchor of cpmRoot.querySelectorAll('li a[href]')) {
  59. let textContent = anchor.textContent.trim()
  60.  
  61. if (addedText.has(textContent)) {
  62. lastText = textContent
  63. continue
  64. }
  65.  
  66. let li = nav.appendChild(document.createElement('li'))
  67. li.appendChild(anchor)
  68.  
  69. addedText.add(textContent)
  70. }
  71.  
  72. if (lastText !== null) {
  73. nav.querySelectorAll('li').forEach(li => {
  74. if (li.querySelector('a').textContent.trim() === lastText) {
  75. li.remove()
  76. }
  77. })
  78. }
  79.  
  80. let tm = document.createElement('template')
  81. tm.innerHTML = `
  82. <li class="with-submenu" style="display: block;">
  83. <a href="#" onclick="return false">${namespace ? namespace : h.textContent}</a>
  84. <nav style="min-width: initial;">
  85. ${nav.innerHTML}
  86. </nav>
  87. </li>
  88. `.trim()
  89.  
  90. return tm.content
  91. }
  92.  
  93. function bufferToHex(buffer) {
  94. const byteArray = new Uint8Array(buffer)
  95. const len = byteArray.length
  96. const hexCodes = new Array(len * 2)
  97. const chars = '0123456789abcdef'
  98. for (let i = 0, j = 0; i < len; i++) {
  99. const byte = byteArray[i]
  100. hexCodes[j++] = chars[byte >> 4]
  101. hexCodes[j++] = chars[byte & 0x0F]
  102. }
  103. return hexCodes.join('')
  104. }
  105.  
  106. async function digestMessage(message) {
  107. const encoder = new TextEncoder("utf-8")
  108. const msgUint8 = encoder.encode(message)
  109. const hashBuffer = await crypto.subtle.digest('SHA-1', msgUint8)
  110. return bufferToHex(hashBuffer)
  111. }
  112.  
  113. async function fetchHTML(href) {
  114. let response = await fetch(href, {
  115. method: "GET",
  116. mode: "same-origin",
  117. cache: "force-cache",
  118. credentials: "same-origin",
  119. redirect: "follow",
  120. referrerPolicy: "no-referrer",
  121. })
  122.  
  123. return response.text()
  124. }
  125.  
  126. async function addSectionsToNav() {
  127. let presetup = preSetup()
  128. if (!presetup) return
  129. const { pos } = presetup
  130.  
  131. let plink = document.querySelector('.user-profile-link')
  132. if (!plink) return
  133. let href = plink.querySelector('a[href*="/users/"]').href
  134. if (href.includes('/users/sign')) return
  135.  
  136. let dm = await digestMessage(href)
  137. const stKey = `gf_user_page_${dm}`
  138.  
  139. for (let trialN = 8; trialN--;) {
  140. let s = sessionStorage.getItem(stKey)
  141. let d = typeof s === 'string' ? parseInt(s) : 0
  142. if (d > 9 && Date.now() - d < 8000) await new Promise(r => setTimeout(r, 320))
  143. else break
  144. }
  145.  
  146. const userPageHTML = await fetchHTML(href)
  147. if (!userPageHTML || typeof userPageHTML !== 'string') return
  148.  
  149. sessionStorage.setItem(stKey, userPageHTML)
  150.  
  151. let template = document.createElement('template')
  152. template.innerHTML = userPageHTML
  153. const content = template.content
  154.  
  155.  
  156.  
  157. sections.forEach(({ id, name }) => {
  158. let section = content.querySelector(id)
  159. if (section) {
  160. const kc = setup({ cpmRoot: section, pos }, name)
  161. if (kc) {
  162. pos.parentNode.insertBefore(kc, pos.nextSibling)
  163. }
  164. }
  165. })
  166. }
  167.  
  168. if (!document.querySelector('.sign-out-link') || document.querySelector('.sign-in-link')) return
  169.  
  170. await addSectionsToNav()
  171. })()