Git Markdown Content Navigation

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

06.02.2021 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Git Markdown Content Navigation
// @name:zh-CN   Git Markdown 文件内容导航
// @namespace    https://github.com/wang1212/github-markdown-file-content-navigation
// @version      0.2.0
// @description  Provide directory navigation of the markdown file content of the github/gitee website.
// @description:zh-cn 提供 github/gitee 网站 markdown 文件内容的目录导航。
// @author       wang1212
// @match        http*://github.com/*
// @match        http*://gitee.com/*
// @grant        none
// ==/UserScript==

;(function () {
	'use strict'

	const log = console.log

	/* ------------------------------- Init ----------------------------- */

	const href = location.href

	const matchGithub = /github/
	const matchGithubRepository = /https?:\/\/github.com\/.+\/.+/
	const matchGitee = /gitee/
	const matchGiteeRepository = /https?:\/\/gitee.com\/.+\/.+/

	const isGithub = !!href.match(matchGithub)
	const isGitee = !!href.match(matchGitee)
	const isRepositoryPage = !!(href.match(matchGithubRepository) || href.match(matchGiteeRepository))

	/* ------------------------------- Parse MarkDown file content navigation ----------------------------- */

	function updateMarkdownFileContentNavigation() {
		let navBarElem = document.querySelector('.wang1212_md-content-nav')

		// Remove existing
		navBarElem && navBarElem.remove()

		if (!isRepositoryPage) return

		// titles
		const titles = getMarkDownContentTitles()
		if (!titles.length) return

		// navBar button
		navBarElem = document.createElement('div')
		navBarElem.classList.add('wang1212_md-content-nav')
		navBarElem.title = 'Markdown 文件内容导航'

		navBarElem.innerText = 'N'

		// Panel
		const navBarPanelElem = document.createElement('div')
		navBarPanelElem.classList.add('wang1212_md-content-nav_panel')

		navBarPanelElem.innerHTML = ''

		// draw titles
		titles.forEach((title) => {
			const level = +title.tagName.substr(-1)
			navBarPanelElem.innerHTML += `
<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(
				2
			)}rem" data-anchor="${title.anchorId}">
${title.text}
</p>
`
		})

		// --- CSS Style ---
		const styleElem = document.createElement('style')
		styleElem.type = 'text/css'
		styleElem.innerHTML = `
.wang1212_md-content-nav {
position: fixed;
right: 1rem;
bottom: 3.5rem;
z-index: 1999;
width: 2rem;
height: 2rem;
color: white;
font-size: 1.5rem;
line-height: 2rem;
text-align: center;
background-color: rgb(36, 41, 46);
cursor: pointer;
}
.wang1212_md-content-nav_panel {
position: absolute;
right: 0;
bottom: 2rem;
display: block;
width: 20rem;
height: 75vh;
padding: 0.5rem;
overflow: auto;
color: #ddd;
text-align: left;
background: white;
box-shadow: rgba(0, 0, 0, 0.25) 0 0 0.5rem 0;
}
.wang1212_md-content-nav_to-anchor {
line-height: 1.6 !important;
}
.wang1212_md-content-nav_to-anchor:hover {
	color: rgb(0, 0, 0);
}
`

		navBarElem.appendChild(navBarPanelElem)
		document.body.appendChild(navBarElem)
		document.head.appendChild(styleElem)

		// --- Event ---
		// Show/Hide
		navBarElem.addEventListener(
			'click',
			(e) => {
				if (e.target !== navBarElem) return

				if (navBarPanelElem.style.display === 'none') {
					navBarPanelElem.style.display = 'block'
				} else {
					navBarPanelElem.style.display = 'none'
				}
			},
			false
		)

		// fly to view
		navBarPanelElem.addEventListener(
			'click',
			(e) => {
				if (!e.target.classList.contains('wang1212_md-content-nav_to-anchor')) return

				const anchorElem = document.getElementById(e.target.dataset.anchor)
				if (!anchorElem) return

				anchorElem.scrollIntoView({ behavior: 'smooth', block: 'start' })
			},
			false
		)
	}

	/* ------------------------------- To Top ----------------------------- */

	// to top button
	function updateGoToTopButton() {
		let toTopElem = document.querySelector('.wang1212_to-top')

		// Remove existing
		toTopElem && toTopElem.remove()

		// toTop button
		toTopElem = document.createElement('div')
		toTopElem.classList.add('wang1212_to-top')
		toTopElem.title = '回到顶部'

		toTopElem.innerText = '↑'

		// --- CSS Style ---
		const styleElem = document.createElement('style')
		styleElem.type = 'text/css'
		styleElem.innerHTML = `
.wang1212_to-top {
position: fixed;
right: 1rem;
bottom: 1rem;
z-index: 1999;
width: 2rem;
height: 2rem;
color: white;
font-size: 1.5rem;
line-height: 2rem;
text-align: center;
background-color: rgb(36, 41, 46);
cursor: pointer;
}
`

		document.body.appendChild(toTopElem)
		document.head.appendChild(styleElem)

		// --- Event ---
		// fly to view
		toTopElem.addEventListener(
			'click',
			() => {
				document.body.scrollIntoView({ behavior: 'smooth' })
			},
			false
		)
	}

	/* ------------------------------- Utils ----------------------------- */

	// parse titles
	function getMarkDownContentTitles() {
		let rootElem = document.querySelector('.markdown-body')

		if (!rootElem) return []

		const anchors = rootElem.querySelectorAll('a.anchor')

		if (!anchors.length) return []

		const titles = []

		anchors.forEach((elem) => {
			const parentElem = elem.parentElement

			titles.push({
				tagName: parentElem.tagName,
				text: parentElem.textContent,
				anchorId: elem.id,
			})
		})

		return titles
	}

	/* ------------------------------- Load ----------------------------- */

	function load() {
		updateMarkdownFileContentNavigation()
		updateGoToTopButton()
	}

	// Monitor page reload
	document.addEventListener('pjax:end', load, false)

	if (isGitee) {
		// Monitor page modify
		const observer = new MutationObserver(load)

		observer.observe(document.querySelector('.tree-holder'), { childList: true, subtree: false })
	}

	//
	load()
})()