syntax highlight + TOC scroll + other content parsing improvements

pull/1/head
NGPixel 10 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,6 +126,7 @@ const parseTree = (content) => {
}; };
let lastNodePath = GetNodePath(); let lastNodePath = GetNodePath();
let lastNode = _.get(tree[treeLength], lastNodePath); let lastNode = _.get(tree[treeLength], lastNodePath);
if(lastNode) {
lastNode.push({ lastNode.push({
content: v.content, content: v.content,
anchor: v.anchor, anchor: v.anchor,
@ -105,16 +134,34 @@ const parseTree = (content) => {
}); });
_.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