浏览代码

syntax highlight + TOC scroll + other content parsing improvements

NGPixel 8 年之前
父节点
当前提交
e94abf9466

文件差异内容过多而无法显示
+ 0 - 0
assets/css/app.css


文件差异内容过多而无法显示
+ 0 - 0
assets/css/libs.css


+ 1 - 1
assets/js/app.js

@@ -1 +1 @@
-"use strict";jQuery(document).ready(function(e){e("a").smoothScroll({speed:"auto"})});
+"use strict";jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});new Sticky(".stickyscroll")});

文件差异内容过多而无法显示
+ 0 - 0
assets/js/libs.js


+ 4 - 1
client/js/app.js

@@ -3,7 +3,10 @@
 jQuery( document ).ready(function( $ ) {
 
 	$('a').smoothScroll({
-		speed: 'auto'
+		speed: 400,
+		offset: -20
 	});
 
+	var sticky = new Sticky('.stickyscroll');
+
 });

+ 1 - 1
client/scss/app.scss

@@ -8,4 +8,4 @@ $warning: #f68b39;
 
 @import './layout/_header';
 @import './layout/_footer';
-
+@import './layout/_content';

+ 32 - 0
client/scss/layout/_content.scss

@@ -0,0 +1,32 @@
+
+.mkcontent {
+
+	h1 {
+		border-bottom: 1px dotted $grey-light;
+		padding-bottom: 4px;
+		font-weight: 400;
+		color: $grey-dark;
+	}
+
+	a.toc-anchor {
+		font-size: 80%;
+		color: $purple;
+		border-bottom: none;
+	}
+
+	.hljs {
+		padding: 0;
+		border-bottom: 1px solid $grey-light;
+		border-right: 1px solid $grey-light;
+		border-radius: 3px;
+	}
+
+	pre + p {
+		padding-top: 1em;
+	}
+
+	img.right {
+		float:right;
+	}
+
+}

+ 1 - 1
controllers/pages.js

@@ -11,7 +11,7 @@ router.get('/', (req, res) => {
 	var Promise = require('bluebird');
 	var fs = Promise.promisifyAll(require("fs"));
 
-	fs.readFileAsync("repo/Gollum.md", "utf8").then(function(contents) {
+	fs.readFileAsync("repo/Home.md", "utf8").then(function(contents) {
 		let pageData = mark.parse(contents);
 		if(!pageData.title) {
 			pageData.title = 'Gollum';

+ 4 - 2
gulpfile.js

@@ -22,7 +22,8 @@ var paths = {
 		'./node_modules/lodash/lodash.min.js',
       './node_modules/jquery/dist/jquery.min.js',
       './node_modules/vue/dist/vue.min.js',
-      './node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js'
+      './node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
+      './node_modules/sticky-js/dist/sticky.min.js'
 	],
 	scriptapps: [
 		'./client/js/components/*.js',
@@ -32,7 +33,8 @@ var paths = {
 		'./client/js/**/*.js'
 	],
 	csslibs: [
-		'./node_modules/font-awesome/css/font-awesome.min.css'
+		'./node_modules/font-awesome/css/font-awesome.min.css',
+		'./node_modules/highlight.js/styles/default.css'
 	],
 	cssapps: [
 		'./client/scss/app.scss'

+ 57 - 10
models/markdown.js

@@ -9,7 +9,10 @@ var Promise = require('bluebird'),
 	mdFootnote = require('markdown-it-footnote'),
 	mdExternalLinks = require('markdown-it-external-links'),
 	mdExpandTabs = require('markdown-it-expand-tabs'),
+	mdAttrs = require('markdown-it-attrs'),
+	hljs = require('highlight.js'),
 	slug = require('slug'),
+	cheerio = require('cheerio'),
 	_ = require('lodash');
 
 // Load plugins
@@ -17,7 +20,15 @@ var Promise = require('bluebird'),
 var mkdown = md({
 		html: true,
 		linkify: true,
-		typography: true
+		typography: true,
+		highlight: function (str, lang) {
+			if (lang && hljs.getLanguage(lang)) {
+				try {
+					return '<pre class="hljs"><code>' + hljs.highlight(lang, str, true).value + '</code></pre>';
+				} catch (__) {}
+			}
+			return '<pre class="hljs"><code>' + hljs.highlightAuto(str).value + '</code></pre>';
+		}
 	})
 	.use(mdEmoji)
 	.use(mdTaskLists)
@@ -33,7 +44,8 @@ var mkdown = md({
 	})
 	.use(mdExpandTabs, {
 		tabWidth: 4
-	});
+	})
+	.use(mdAttrs);
 
 // Rendering rules
 
@@ -41,13 +53,23 @@ mkdown.renderer.rules.emoji = function(token, idx) {
   return '<i class="twa twa-' + token[idx].markup + '"></i>';
 };
 
-// Parse markdown headings tree
+mkdown.inline.ruler.push('internal_link', (state) => {
 
+});
+
+/**
+ * Parse markdown content and build TOC tree
+ *
+ * @param      {(Function|string)}  content  Markdown content
+ * @return     {Array}             TOC tree
+ */
 const parseTree = (content) => {
 
 	let tokens = md().parse(content, {});
 	let tocArray = [];
 
+	//-> Extract headings and their respective levels
+
 	for (let i = 0; i < tokens.length; i++) {
 		if (tokens[i].type !== "heading_close") {
 		  continue
@@ -75,6 +97,12 @@ const parseTree = (content) => {
 		}
 	 }
 
+	 //-> Exclude levels deeper than 2
+
+	 _.remove(tocArray, (n) => { return n.level > 2; });
+
+	 //-> Build tree from flat array
+
 	 return _.reduce(tocArray, (tree, v) => {
 		let treeLength = tree.length - 1;
 		if(v.level < 2) {
@@ -98,23 +126,42 @@ const parseTree = (content) => {
 			};
 			let lastNodePath = GetNodePath();
 			let lastNode = _.get(tree[treeLength], lastNodePath);
-			lastNode.push({
-				content: v.content,
-				anchor: v.anchor,
-				nodes: []
-			});
-			_.set(tree[treeLength], lastNodePath, lastNode);
+			if(lastNode) {
+				lastNode.push({
+					content: v.content,
+					anchor: v.anchor,
+					nodes: []
+				});
+				_.set(tree[treeLength], lastNodePath, lastNode);
+			}
 		}
 		return tree;
 	}, []);
 
 };
 
+/**
+ * Parse markdown content to HTML
+ *
+ * @param      {String}    content  Markdown content
+ * @return     {String}  HTML formatted content
+ */
+const parseContent = (content)  => {
+
+	let output = mkdown.render(content);
+	let cr = cheerio.load(output);
+	cr('table').addClass('table is-bordered is-striped is-narrow');
+	output = cr.html();
+
+	return output;
+
+};
+
 module.exports = {
 
 	parse(content) {
 		return {
-			html: mkdown.render(content),
+			html: parseContent(content),
 			tree: parseTree(content)
 		};
 	}

+ 4 - 0
package.json

@@ -35,6 +35,7 @@
     "bluebird": "^3.4.1",
     "body-parser": "^1.15.2",
     "bulma": "^0.1.2",
+    "cheerio": "^0.20.0",
     "compression": "^1.6.2",
     "connect-flash": "^0.1.1",
     "connect-redis": "^3.1.0",
@@ -44,6 +45,7 @@
     "express-brute-redis": "0.0.1",
     "express-session": "^1.14.0",
     "express-validator": "^2.20.8",
+    "highlight.js": "^9.6.0",
     "i18next": "^3.4.1",
     "i18next-express-middleware": "^1.0.1",
     "i18next-node-fs-backend": "^0.1.2",
@@ -53,6 +55,7 @@
     "markdown-it": "^7.0.1",
     "markdown-it-abbr": "^1.0.3",
     "markdown-it-anchor": "^2.5.0",
+    "markdown-it-attrs": "^0.6.3",
     "markdown-it-emoji": "^1.2.0",
     "markdown-it-expand-tabs": "^1.0.11",
     "markdown-it-external-links": "0.0.5",
@@ -71,6 +74,7 @@
     "serve-favicon": "^2.3.0",
     "simplemde": "^1.11.2",
     "slug": "^0.9.1",
+    "sticky-js": "^1.0.5",
     "twemoji-awesome": "^1.0.4",
     "validator": "^5.5.0",
     "validator-as-promised": "^1.0.2",

+ 1 - 1
views/common/header.pug

@@ -1,5 +1,5 @@
 
-nav.nav.has-shadow
+nav.nav.has-shadow.stickyscroll
 	.nav-left
 		a.nav-item.is-brand(href='/')
 			img(src='/favicons/android-icon-96x96.png', alt='Wiki')

+ 6 - 5
views/pages/view.pug

@@ -25,18 +25,19 @@ block content
 									a(href='/') Home
 								li
 									a(href='/account') Account
-					.box
+					.box.stickyscroll(data-margin-top=70)
 						aside.menu(style= { 'min-width': '200px' })
 							p.menu-label
 								| Contents
 							ul.menu-list
+								a(href='#root', title='Start') Start
 								+tocMenu(pageData.tree)
 
 				.column
 
-					h1.title= pageData.title
-					h2.subtitle
-						| Primary bold subtitle
-					.content
+					h1.title#title= pageData.title
+					if pageData.subtitle
+						h2.subtitle= pageData.subtitle
+					.content.mkcontent
 						!= pageData.html
 

部分文件因为文件数量过多而无法显示