syntax highlight + TOC scroll + other content parsing improvements

This commit is contained in:
NGPixel 2016-08-20 23:28:53 -04:00
parent 1ad03a3d1f
commit e94abf9466
13 changed files with 114 additions and 25 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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")});

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -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';

View File

@ -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'

View File

@ -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)
};
}

View File

@ -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",

View File

@ -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')

View File

@ -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