syntax highlight + TOC scroll + other content parsing improvements

pull/1/head
NGPixel 9 years ago
parent 1ad03a3d1f
commit e94abf9466

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

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

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

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

@ -11,7 +11,7 @@ router.get('/', (req, res) => {
var Promise = require('bluebird'); var Promise = require('bluebird');
var fs = Promise.promisifyAll(require("fs")); 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); let pageData = mark.parse(contents);
if(!pageData.title) { if(!pageData.title) {
pageData.title = 'Gollum'; pageData.title = 'Gollum';

@ -22,7 +22,8 @@ var paths = {
'./node_modules/lodash/lodash.min.js', './node_modules/lodash/lodash.min.js',
'./node_modules/jquery/dist/jquery.min.js', './node_modules/jquery/dist/jquery.min.js',
'./node_modules/vue/dist/vue.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: [ scriptapps: [
'./client/js/components/*.js', './client/js/components/*.js',
@ -32,7 +33,8 @@ var paths = {
'./client/js/**/*.js' './client/js/**/*.js'
], ],
csslibs: [ 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: [ cssapps: [
'./client/scss/app.scss' './client/scss/app.scss'

@ -9,7 +9,10 @@ var Promise = require('bluebird'),
mdFootnote = require('markdown-it-footnote'), mdFootnote = require('markdown-it-footnote'),
mdExternalLinks = require('markdown-it-external-links'), mdExternalLinks = require('markdown-it-external-links'),
mdExpandTabs = require('markdown-it-expand-tabs'), mdExpandTabs = require('markdown-it-expand-tabs'),
mdAttrs = require('markdown-it-attrs'),
hljs = require('highlight.js'),
slug = require('slug'), slug = require('slug'),
cheerio = require('cheerio'),
_ = require('lodash'); _ = require('lodash');
// Load plugins // Load plugins
@ -17,7 +20,15 @@ var Promise = require('bluebird'),
var mkdown = md({ var mkdown = md({
html: true, html: true,
linkify: 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(mdEmoji)
.use(mdTaskLists) .use(mdTaskLists)
@ -33,7 +44,8 @@ var mkdown = md({
}) })
.use(mdExpandTabs, { .use(mdExpandTabs, {
tabWidth: 4 tabWidth: 4
}); })
.use(mdAttrs);
// Rendering rules // Rendering rules
@ -41,13 +53,23 @@ mkdown.renderer.rules.emoji = function(token, idx) {
return '<i class="twa twa-' + token[idx].markup + '"></i>'; 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) => { const parseTree = (content) => {
let tokens = md().parse(content, {}); let tokens = md().parse(content, {});
let tocArray = []; let tocArray = [];
//-> Extract headings and their respective levels
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type !== "heading_close") { if (tokens[i].type !== "heading_close") {
continue 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) => { return _.reduce(tocArray, (tree, v) => {
let treeLength = tree.length - 1; let treeLength = tree.length - 1;
if(v.level < 2) { if(v.level < 2) {
@ -98,23 +126,42 @@ const parseTree = (content) => {
}; };
let lastNodePath = GetNodePath(); let lastNodePath = GetNodePath();
let lastNode = _.get(tree[treeLength], lastNodePath); let lastNode = _.get(tree[treeLength], lastNodePath);
lastNode.push({ if(lastNode) {
content: v.content, lastNode.push({
anchor: v.anchor, content: v.content,
nodes: [] anchor: v.anchor,
}); nodes: []
_.set(tree[treeLength], lastNodePath, lastNode); });
_.set(tree[treeLength], lastNodePath, lastNode);
}
} }
return tree; 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 = { module.exports = {
parse(content) { parse(content) {
return { return {
html: mkdown.render(content), html: parseContent(content),
tree: parseTree(content) tree: parseTree(content)
}; };
} }

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

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

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

Loading…
Cancel
Save