Git Markdown 文件内容导航

Provide directory navigation of the markdown file content of the github/gitee website.

当前为 2021-02-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Git Markdown Content Navigation
  3. // @name:zh-CN Git Markdown 文件内容导航
  4. // @namespace https://github.com/wang1212/github-markdown-file-content-navigation
  5. // @version 0.2.0
  6. // @description Provide directory navigation of the markdown file content of the github/gitee website.
  7. // @description:zh-cn 提供 github/gitee 网站 markdown 文件内容的目录导航。
  8. // @author wang1212
  9. // @match http*://github.com/*
  10. // @match http*://gitee.com/*
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. ;(function () {
  15. 'use strict'
  16.  
  17. const log = console.log
  18.  
  19. /* ------------------------------- Init ----------------------------- */
  20.  
  21. const href = location.href
  22.  
  23. const matchGithub = /github/
  24. const matchGithubRepository = /https?:\/\/github.com\/.+\/.+/
  25. const matchGitee = /gitee/
  26. const matchGiteeRepository = /https?:\/\/gitee.com\/.+\/.+/
  27.  
  28. const isGithub = !!href.match(matchGithub)
  29. const isGitee = !!href.match(matchGitee)
  30. const isRepositoryPage = !!(href.match(matchGithubRepository) || href.match(matchGiteeRepository))
  31.  
  32. /* ------------------------------- Parse MarkDown file content navigation ----------------------------- */
  33.  
  34. function updateMarkdownFileContentNavigation() {
  35. let navBarElem = document.querySelector('.wang1212_md-content-nav')
  36.  
  37. // Remove existing
  38. navBarElem && navBarElem.remove()
  39.  
  40. if (!isRepositoryPage) return
  41.  
  42. // titles
  43. const titles = getMarkDownContentTitles()
  44. if (!titles.length) return
  45.  
  46. // navBar button
  47. navBarElem = document.createElement('div')
  48. navBarElem.classList.add('wang1212_md-content-nav')
  49. navBarElem.title = 'Markdown 文件内容导航'
  50.  
  51. navBarElem.innerText = 'N'
  52.  
  53. // Panel
  54. const navBarPanelElem = document.createElement('div')
  55. navBarPanelElem.classList.add('wang1212_md-content-nav_panel')
  56.  
  57. navBarPanelElem.innerHTML = ''
  58.  
  59. // draw titles
  60. titles.forEach((title) => {
  61. const level = +title.tagName.substr(-1)
  62. navBarPanelElem.innerHTML += `
  63. <p class="wang1212_md-content-nav_to-anchor" style="font-size: ${1 - ((level - 1) * 0).toFixed(2)}rem; margin: 0; padding-left: ${((level - 1) * 0.5).toFixed(
  64. 2
  65. )}rem" data-anchor="${title.anchorId}">
  66. ${title.text}
  67. </p>
  68. `
  69. })
  70.  
  71. // --- CSS Style ---
  72. const styleElem = document.createElement('style')
  73. styleElem.type = 'text/css'
  74. styleElem.innerHTML = `
  75. .wang1212_md-content-nav {
  76. position: fixed;
  77. right: 1rem;
  78. bottom: 3.5rem;
  79. z-index: 1999;
  80. width: 2rem;
  81. height: 2rem;
  82. color: white;
  83. font-size: 1.5rem;
  84. line-height: 2rem;
  85. text-align: center;
  86. background-color: rgb(36, 41, 46);
  87. cursor: pointer;
  88. }
  89. .wang1212_md-content-nav_panel {
  90. position: absolute;
  91. right: 0;
  92. bottom: 2rem;
  93. display: block;
  94. width: 20rem;
  95. height: 75vh;
  96. padding: 0.5rem;
  97. overflow: auto;
  98. color: #ddd;
  99. text-align: left;
  100. background: white;
  101. box-shadow: rgba(0, 0, 0, 0.25) 0 0 0.5rem 0;
  102. }
  103. .wang1212_md-content-nav_to-anchor {
  104. line-height: 1.6 !important;
  105. }
  106. .wang1212_md-content-nav_to-anchor:hover {
  107. color: rgb(0, 0, 0);
  108. }
  109. `
  110.  
  111. navBarElem.appendChild(navBarPanelElem)
  112. document.body.appendChild(navBarElem)
  113. document.head.appendChild(styleElem)
  114.  
  115. // --- Event ---
  116. // Show/Hide
  117. navBarElem.addEventListener(
  118. 'click',
  119. (e) => {
  120. if (e.target !== navBarElem) return
  121.  
  122. if (navBarPanelElem.style.display === 'none') {
  123. navBarPanelElem.style.display = 'block'
  124. } else {
  125. navBarPanelElem.style.display = 'none'
  126. }
  127. },
  128. false
  129. )
  130.  
  131. // fly to view
  132. navBarPanelElem.addEventListener(
  133. 'click',
  134. (e) => {
  135. if (!e.target.classList.contains('wang1212_md-content-nav_to-anchor')) return
  136.  
  137. const anchorElem = document.getElementById(e.target.dataset.anchor)
  138. if (!anchorElem) return
  139.  
  140. anchorElem.scrollIntoView({ behavior: 'smooth', block: 'start' })
  141. },
  142. false
  143. )
  144. }
  145.  
  146. /* ------------------------------- To Top ----------------------------- */
  147.  
  148. // to top button
  149. function updateGoToTopButton() {
  150. let toTopElem = document.querySelector('.wang1212_to-top')
  151.  
  152. // Remove existing
  153. toTopElem && toTopElem.remove()
  154.  
  155. // toTop button
  156. toTopElem = document.createElement('div')
  157. toTopElem.classList.add('wang1212_to-top')
  158. toTopElem.title = '回到顶部'
  159.  
  160. toTopElem.innerText = '↑'
  161.  
  162. // --- CSS Style ---
  163. const styleElem = document.createElement('style')
  164. styleElem.type = 'text/css'
  165. styleElem.innerHTML = `
  166. .wang1212_to-top {
  167. position: fixed;
  168. right: 1rem;
  169. bottom: 1rem;
  170. z-index: 1999;
  171. width: 2rem;
  172. height: 2rem;
  173. color: white;
  174. font-size: 1.5rem;
  175. line-height: 2rem;
  176. text-align: center;
  177. background-color: rgb(36, 41, 46);
  178. cursor: pointer;
  179. }
  180. `
  181.  
  182. document.body.appendChild(toTopElem)
  183. document.head.appendChild(styleElem)
  184.  
  185. // --- Event ---
  186. // fly to view
  187. toTopElem.addEventListener(
  188. 'click',
  189. () => {
  190. document.body.scrollIntoView({ behavior: 'smooth' })
  191. },
  192. false
  193. )
  194. }
  195.  
  196. /* ------------------------------- Utils ----------------------------- */
  197.  
  198. // parse titles
  199. function getMarkDownContentTitles() {
  200. let rootElem = document.querySelector('.markdown-body')
  201.  
  202. if (!rootElem) return []
  203.  
  204. const anchors = rootElem.querySelectorAll('a.anchor')
  205.  
  206. if (!anchors.length) return []
  207.  
  208. const titles = []
  209.  
  210. anchors.forEach((elem) => {
  211. const parentElem = elem.parentElement
  212.  
  213. titles.push({
  214. tagName: parentElem.tagName,
  215. text: parentElem.textContent,
  216. anchorId: elem.id,
  217. })
  218. })
  219.  
  220. return titles
  221. }
  222.  
  223. /* ------------------------------- Load ----------------------------- */
  224.  
  225. function load() {
  226. updateMarkdownFileContentNavigation()
  227. updateGoToTopButton()
  228. }
  229.  
  230. // Monitor page reload
  231. document.addEventListener('pjax:end', load, false)
  232.  
  233. if (isGitee) {
  234. // Monitor page modify
  235. const observer = new MutationObserver(load)
  236.  
  237. observer.observe(document.querySelector('.tree-holder'), { childList: true, subtree: false })
  238. }
  239.  
  240. //
  241. load()
  242. })()

QingJ © 2025

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