Caching + Edit Mode UI
This commit is contained in:
parent
1d2893765c
commit
4be54310c4
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";function _classCallCheck(e,s){if(!(e instanceof s))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,s){for(var t=0;t<s.length;t++){var n=s[t];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(s,t,n){return t&&e(s.prototype,t),n&&e(s,n),s}}(),Alerts=function(){function e(){_classCallCheck(this,e);var s=this;s.mdl=new Vue({el:"#alerts",data:{children:[]},methods:{acknowledge:function(e){s.close(e)}}}),s.uidNext=1}return _createClass(e,[{key:"push",value:function(e){var s=this,t=_.defaults(e,{_uid:s.uidNext,class:"is-info",message:"---",sticky:!1,title:"---"});s.mdl.children.push(t),t.sticky||_.delay(function(){s.close(t._uid)},5e3),s.uidNext++}},{key:"pushError",value:function(e,s){this.push({class:"is-danger",message:s,sticky:!1,title:e})}},{key:"pushSuccess",value:function(e,s){this.push({class:"is-success",message:s,sticky:!1,title:e})}},{key:"close",value:function(e){var s=this,t=_.findIndex(s.mdl.children,["_uid",e]),n=_.nth(s.mdl.children,t);t>=0&&n&&(n.class+=" exit",s.mdl.children.$set(t,n),_.delay(function(){s.mdl.children.$remove(n)},500))}}]),e}();jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});var s=(new Sticky(".stickyscroll"),new Alerts);alertsData&&_.forEach(alertsData,function(e){s.push(e)})});
|
"use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var s=t[n];s.enumerable=s.enumerable||!1,s.configurable=!0,"value"in s&&(s.writable=!0),Object.defineProperty(e,s.key,s)}}return function(t,n,s){return n&&e(t.prototype,n),s&&e(t,s),t}}(),Alerts=function(){function e(){_classCallCheck(this,e);var t=this;t.mdl=new Vue({el:"#alerts",data:{children:[]},methods:{acknowledge:function(e){t.close(e)}}}),t.uidNext=1}return _createClass(e,[{key:"push",value:function(e){var t=this,n=_.defaults(e,{_uid:t.uidNext,class:"is-info",message:"---",sticky:!1,title:"---"});t.mdl.children.push(n),n.sticky||_.delay(function(){t.close(n._uid)},5e3),t.uidNext++}},{key:"pushError",value:function(e,t){this.push({class:"is-danger",message:t,sticky:!1,title:e})}},{key:"pushSuccess",value:function(e,t){this.push({class:"is-success",message:t,sticky:!1,title:e})}},{key:"close",value:function(e){var t=this,n=_.findIndex(t.mdl.children,["_uid",e]),s=_.nth(t.mdl.children,n);n>=0&&s&&(s.class+=" exit",t.mdl.children.$set(n,s),_.delay(function(){t.mdl.children.$remove(s)},500))}}]),e}();jQuery(document).ready(function(e){e("a").smoothScroll({speed:400,offset:-20});var t=(new Sticky(".stickyscroll"),new Alerts);if(alertsData&&_.forEach(alertsData,function(e){t.push(e)}),1===e("#mk-editor").length){new SimpleMDE({autofocus:!0,element:e("#mk-editor").get(0),autoDownloadFontAwesome:!1,placeholder:"Enter Markdown formatted content here...",hideIcons:["heading","quote"],showIcons:["strikethrough","heading-1","heading-2","heading-3","code","table","horizontal-rule"],spellChecker:!1})}});
|
File diff suppressed because one or more lines are too long
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
jQuery( document ).ready(function( $ ) {
|
jQuery( document ).ready(function( $ ) {
|
||||||
|
|
||||||
|
// Scroll
|
||||||
|
|
||||||
$('a').smoothScroll({
|
$('a').smoothScroll({
|
||||||
speed: 400,
|
speed: 400,
|
||||||
offset: -20
|
offset: -20
|
||||||
@ -9,6 +11,8 @@ jQuery( document ).ready(function( $ ) {
|
|||||||
|
|
||||||
var sticky = new Sticky('.stickyscroll');
|
var sticky = new Sticky('.stickyscroll');
|
||||||
|
|
||||||
|
// Alerts
|
||||||
|
|
||||||
var alerts = new Alerts();
|
var alerts = new Alerts();
|
||||||
if(alertsData) {
|
if(alertsData) {
|
||||||
_.forEach(alertsData, (alertRow) => {
|
_.forEach(alertsData, (alertRow) => {
|
||||||
@ -16,4 +20,20 @@ jQuery( document ).ready(function( $ ) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
|
||||||
|
if($('#mk-editor').length === 1) {
|
||||||
|
|
||||||
|
let mde = new SimpleMDE({
|
||||||
|
autofocus: true,
|
||||||
|
element: $("#mk-editor").get(0),
|
||||||
|
autoDownloadFontAwesome: false,
|
||||||
|
placeholder: 'Enter Markdown formatted content here...',
|
||||||
|
hideIcons: ['heading', 'quote'],
|
||||||
|
showIcons: ['strikethrough', 'heading-1', 'heading-2', 'heading-3', 'code', 'table', 'horizontal-rule'],
|
||||||
|
spellChecker: false
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
@ -1,13 +1,20 @@
|
|||||||
//@import './layout/_fonts';
|
//@import './layout/_fonts';
|
||||||
@import './layout/_base';
|
@import './layout/_base';
|
||||||
|
|
||||||
$warning: #f68b39;
|
$red: #E53935;
|
||||||
|
$orange: #FB8C00;
|
||||||
|
$blue: #039BE5;
|
||||||
|
$turquoise: #00ACC1;
|
||||||
|
$green: #7CB342;
|
||||||
|
|
||||||
|
$warning: $orange;
|
||||||
|
|
||||||
@import 'bulma';
|
@import 'bulma';
|
||||||
@import './libs/twemoji-awesome';
|
@import './libs/twemoji-awesome';
|
||||||
@import './libs/animate.min.css';
|
@import './libs/animate.min.css';
|
||||||
|
|
||||||
@import './components/_alerts';
|
@import './components/_alerts';
|
||||||
|
@import './components/_editor';
|
||||||
|
|
||||||
@import './layout/_header';
|
@import './layout/_header';
|
||||||
@import './layout/_footer';
|
@import './layout/_footer';
|
||||||
|
8
client/scss/components/_editor.scss
Normal file
8
client/scss/components/_editor.scss
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
.editor-toolbar i.separator {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-toolbar .fa {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section.is-small {
|
||||||
|
padding: 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.mkcontent {
|
.mkcontent {
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@ -26,12 +30,31 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs {
|
a.external-link {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "\f08e";
|
||||||
|
font-family: FontAwesome;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
text-decoration: inherit;
|
||||||
|
color: $grey;
|
||||||
|
font-size: 14px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
> code {
|
> code {
|
||||||
box-shadow: inset 0 0 5px 0 $grey-light;
|
box-shadow: inset 0 0 5px 0 $grey-light;
|
||||||
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -54,6 +77,10 @@
|
|||||||
color: $grey-dark;
|
color: $grey-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.twa {
|
||||||
|
font-size: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content a:not(.button):visited {
|
.content a:not(.button):visited {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
h2.nav-item {
|
||||||
|
font-size: 150%;
|
||||||
|
color: $orange;
|
||||||
|
}
|
@ -2,9 +2,32 @@
|
|||||||
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = express.Router();
|
var router = express.Router();
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
router.get('/edit/*', (req, res, next) => {
|
router.get('/edit/*', (req, res, next) => {
|
||||||
res.send('EDIT MODE');
|
|
||||||
|
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
|
||||||
|
|
||||||
|
entries.fetchOriginal(safePath, {
|
||||||
|
parseMarkdown: false,
|
||||||
|
parseMeta: true,
|
||||||
|
parseTree: false,
|
||||||
|
includeMarkdown: true,
|
||||||
|
includeParentInfo: false,
|
||||||
|
cache: false
|
||||||
|
}).then((pageData) => {
|
||||||
|
if(pageData) {
|
||||||
|
return res.render('pages/edit', { pageData });
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid page path.');
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
res.render('error', {
|
||||||
|
message: err.message,
|
||||||
|
error: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/new/*', (req, res, next) => {
|
router.get('/new/*', (req, res, next) => {
|
||||||
@ -19,13 +42,13 @@ router.get('/*', (req, res, next) => {
|
|||||||
let safePath = entries.parsePath(req.path);
|
let safePath = entries.parsePath(req.path);
|
||||||
|
|
||||||
entries.fetch(safePath).then((pageData) => {
|
entries.fetch(safePath).then((pageData) => {
|
||||||
console.log(pageData);
|
|
||||||
if(pageData) {
|
if(pageData) {
|
||||||
res.render('pages/view', { pageData });
|
return res.render('pages/view', { pageData });
|
||||||
} else {
|
} else {
|
||||||
next();
|
return next();
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
winston.error(err);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@ var paths = {
|
|||||||
'./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'
|
'./node_modules/sticky-js/dist/sticky.min.js',
|
||||||
|
'./node_modules/simplemde/dist/simplemde.min.js'
|
||||||
],
|
],
|
||||||
scriptapps: [
|
scriptapps: [
|
||||||
'./client/js/components/*.js',
|
'./client/js/components/*.js',
|
||||||
@ -34,7 +35,8 @@ var paths = {
|
|||||||
],
|
],
|
||||||
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'
|
'./node_modules/highlight.js/styles/default.css',
|
||||||
|
'./node_modules/simplemde/dist/simplemde.min.css'
|
||||||
],
|
],
|
||||||
cssapps: [
|
cssapps: [
|
||||||
'./client/scss/app.scss'
|
'./client/scss/app.scss'
|
||||||
|
@ -5,7 +5,8 @@ var Promise = require('bluebird'),
|
|||||||
fs = Promise.promisifyAll(require("fs")),
|
fs = Promise.promisifyAll(require("fs")),
|
||||||
_ = require('lodash'),
|
_ = require('lodash'),
|
||||||
farmhash = require('farmhash'),
|
farmhash = require('farmhash'),
|
||||||
msgpack = require('msgpack5')();
|
BSONModule = require('bson'),
|
||||||
|
BSON = new BSONModule.BSONPure.BSON();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entries Model
|
* Entries Model
|
||||||
@ -32,12 +33,17 @@ module.exports = {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch an entry from cache, otherwise the original
|
||||||
|
*
|
||||||
|
* @param {String} entryPath The entry path
|
||||||
|
* @return {Object} Page Data
|
||||||
|
*/
|
||||||
fetch(entryPath) {
|
fetch(entryPath) {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
let fpath = path.join(self._repoPath, entryPath + '.md');
|
let cpath = path.join(self._cachePath, farmhash.fingerprint32(entryPath) + '.bson');
|
||||||
let cpath = path.join(self._cachePath, farmhash.fingerprint32(entryPath) + '.bin');
|
|
||||||
|
|
||||||
return fs.statAsync(cpath).then((st) => {
|
return fs.statAsync(cpath).then((st) => {
|
||||||
return st.isFile();
|
return st.isFile();
|
||||||
@ -47,10 +53,10 @@ module.exports = {
|
|||||||
|
|
||||||
if(isCache) {
|
if(isCache) {
|
||||||
|
|
||||||
console.log('from cache!');
|
// Load from cache
|
||||||
|
|
||||||
return fs.readFileAsync(cpath, 'utf8').then((contents) => {
|
return fs.readFileAsync(cpath).then((contents) => {
|
||||||
return msgpack.decode(contents);
|
return BSON.deserialize(contents);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
winston.error('Corrupted cache file. Deleting it...');
|
winston.error('Corrupted cache file. Deleting it...');
|
||||||
fs.unlinkSync(cpath);
|
fs.unlinkSync(cpath);
|
||||||
@ -59,38 +65,96 @@ module.exports = {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
console.log('original!');
|
// Load original
|
||||||
|
|
||||||
// Parse original and cache it
|
return self.fetchOriginal(entryPath);
|
||||||
|
|
||||||
return fs.statAsync(fpath).then((st) => {
|
|
||||||
if(st.isFile()) {
|
|
||||||
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
|
|
||||||
let pageData = mark.parse(contents);
|
|
||||||
if(!pageData.meta.title) {
|
|
||||||
pageData.meta.title = entryPath;
|
|
||||||
}
|
|
||||||
let cacheData = msgpack.encode(pageData);
|
|
||||||
return fs.writeFileAsync(cpath, cacheData, { encoding: 'utf8' }).then(() => {
|
|
||||||
return pageData;
|
|
||||||
}).catch((err) => {
|
|
||||||
winston.error('Unable to write to cache! Performance may be affected.');
|
|
||||||
return pageData;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the original document entry
|
||||||
|
*
|
||||||
|
* @param {String} entryPath The entry path
|
||||||
|
* @param {Object} options The options
|
||||||
|
* @return {Object} Page data
|
||||||
|
*/
|
||||||
|
fetchOriginal(entryPath, options) {
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
let fpath = path.join(self._repoPath, entryPath + '.md');
|
||||||
|
let cpath = path.join(self._cachePath, farmhash.fingerprint32(entryPath) + '.bson');
|
||||||
|
|
||||||
|
options = _.defaults(options, {
|
||||||
|
parseMarkdown: true,
|
||||||
|
parseMeta: true,
|
||||||
|
parseTree: true,
|
||||||
|
includeMarkdown: false,
|
||||||
|
includeParentInfo: true,
|
||||||
|
cache: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return fs.statAsync(fpath).then((st) => {
|
||||||
|
if(st.isFile()) {
|
||||||
|
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
|
||||||
|
|
||||||
|
// Parse contents
|
||||||
|
|
||||||
|
let pageData = {
|
||||||
|
markdown: (options.includeMarkdown) ? contents : '',
|
||||||
|
html: (options.parseMarkdown) ? mark.parseContent(contents) : '',
|
||||||
|
meta: (options.parseMeta) ? mark.parseMeta(contents) : {},
|
||||||
|
tree: (options.parseTree) ? mark.parseTree(contents) : []
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!pageData.meta.title) {
|
||||||
|
pageData.meta.title = _.startCase(entryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
pageData.meta.path = entryPath;
|
||||||
|
|
||||||
|
// Get parent
|
||||||
|
|
||||||
|
let parentPromise = (options.includeParentInfo) ? self.getParentInfo(entryPath).then((parentData) => {
|
||||||
|
return (pageData.parent = parentData);
|
||||||
|
}).catch((err) => {
|
||||||
|
return (pageData.parent = false);
|
||||||
|
}) : Promise.resolve(true);
|
||||||
|
|
||||||
|
return parentPromise.then(() => {
|
||||||
|
|
||||||
|
// Cache to disk
|
||||||
|
|
||||||
|
if(options.cache) {
|
||||||
|
let cacheData = BSON.serialize(pageData, false, false, false);
|
||||||
|
return fs.writeFileAsync(cpath, cacheData).catch((err) => {
|
||||||
|
winston.error('Unable to write to cache! Performance may be affected.');
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}).return(pageData);
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse raw url path and make it safe
|
||||||
|
*
|
||||||
|
* @param {String} urlPath The url path
|
||||||
|
* @return {String} Safe entry path
|
||||||
|
*/
|
||||||
parsePath(urlPath) {
|
parsePath(urlPath) {
|
||||||
|
|
||||||
let wlist = new RegExp('[^a-z0-9/\-]','g');
|
let wlist = new RegExp('[^a-z0-9/\-]','g');
|
||||||
@ -105,6 +169,47 @@ module.exports = {
|
|||||||
|
|
||||||
return _.join(urlParts, '/');
|
return _.join(urlParts, '/');
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the parent information.
|
||||||
|
*
|
||||||
|
* @param {String} entryPath The entry path
|
||||||
|
* @return {Object|False} The parent information.
|
||||||
|
*/
|
||||||
|
getParentInfo(entryPath) {
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
if(_.includes(entryPath, '/')) {
|
||||||
|
|
||||||
|
let parentParts = _.split(entryPath, '/');
|
||||||
|
let parentPath = _.join(_.initial(parentParts),'/');
|
||||||
|
let parentFile = _.last(parentParts);
|
||||||
|
let fpath = path.join(self._repoPath, parentPath + '.md');
|
||||||
|
|
||||||
|
return fs.statAsync(fpath).then((st) => {
|
||||||
|
if(st.isFile()) {
|
||||||
|
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
|
||||||
|
|
||||||
|
let pageMeta = mark.parseMeta(contents);
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: parentPath,
|
||||||
|
title: (pageMeta.title) ? pageMeta.title : _.startCase(parentFile),
|
||||||
|
subtitle: (pageMeta.subtitle) ? pageMeta.subtitle : false
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error('Parent entry is not a valid file.'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Promise.reject(new Error('Parent entry is root.'));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
@ -29,7 +29,7 @@ var mkdown = md({
|
|||||||
return '<pre><code>' + str + '</code></pre>';
|
return '<pre><code>' + str + '</code></pre>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '<pre class="hljs"><code>' + hljs.highlightAuto(str).value + '</code></pre>';
|
return '<pre><code>' + str + '</code></pre>';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.use(mdEmoji)
|
.use(mdEmoji)
|
||||||
@ -175,6 +175,10 @@ module.exports = {
|
|||||||
html: parseContent(content),
|
html: parseContent(content),
|
||||||
tree: parseTree(content)
|
tree: parseTree(content)
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
|
parseContent,
|
||||||
|
parseMeta,
|
||||||
|
parseTree
|
||||||
|
|
||||||
};
|
};
|
@ -34,6 +34,7 @@
|
|||||||
"bcryptjs-then": "^1.0.1",
|
"bcryptjs-then": "^1.0.1",
|
||||||
"bluebird": "^3.4.1",
|
"bluebird": "^3.4.1",
|
||||||
"body-parser": "^1.15.2",
|
"body-parser": "^1.15.2",
|
||||||
|
"bson": "^0.5.4",
|
||||||
"bulma": "^0.1.2",
|
"bulma": "^0.1.2",
|
||||||
"cheerio": "^0.22.0",
|
"cheerio": "^0.22.0",
|
||||||
"child-process-promise": "^2.1.3",
|
"child-process-promise": "^2.1.3",
|
||||||
@ -48,6 +49,7 @@
|
|||||||
"express-session": "^1.14.0",
|
"express-session": "^1.14.0",
|
||||||
"express-validator": "^2.20.8",
|
"express-validator": "^2.20.8",
|
||||||
"farmhash": "^1.2.0",
|
"farmhash": "^1.2.0",
|
||||||
|
"fs-extra": "^0.30.0",
|
||||||
"git-wrapper2-promise": "^0.2.9",
|
"git-wrapper2-promise": "^0.2.9",
|
||||||
"highlight.js": "^9.6.0",
|
"highlight.js": "^9.6.0",
|
||||||
"i18next": "^3.4.1",
|
"i18next": "^3.4.1",
|
||||||
@ -69,10 +71,10 @@
|
|||||||
"markdown-it-toc-and-anchor": "^4.1.1",
|
"markdown-it-toc-and-anchor": "^4.1.1",
|
||||||
"moment": "^2.14.1",
|
"moment": "^2.14.1",
|
||||||
"moment-timezone": "^0.5.5",
|
"moment-timezone": "^0.5.5",
|
||||||
"msgpack5": "^3.4.0",
|
|
||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pug": "^2.0.0-beta5",
|
"pug": "^2.0.0-beta5",
|
||||||
|
"search-index": "^0.8.15",
|
||||||
"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",
|
||||||
|
@ -1,30 +1,33 @@
|
|||||||
|
|
||||||
nav.nav.has-shadow.stickyscroll
|
nav.nav.has-shadow.stickyscroll
|
||||||
.nav-left
|
.nav-left
|
||||||
a.nav-item.is-brand(href='/')
|
block rootNavLeft
|
||||||
img(src='/favicons/android-icon-96x96.png', alt='Wiki')
|
a.nav-item.is-brand(href='/')
|
||||||
a.nav-item(href='/')
|
img(src='/favicons/android-icon-96x96.png', alt='Wiki')
|
||||||
h1.title Wiki
|
a.nav-item(href='/')
|
||||||
|
h1.title Wiki
|
||||||
.nav-center
|
.nav-center
|
||||||
p.nav-item
|
block rootNavCenter
|
||||||
input.input(type='text', placeholder='Search...', style= { 'max-width': '300px', width: '33vw' })
|
p.nav-item
|
||||||
|
input.input(type='text', placeholder='Search...', style= { 'max-width': '300px', width: '33vw' })
|
||||||
span.nav-toggle
|
span.nav-toggle
|
||||||
span
|
span
|
||||||
span
|
span
|
||||||
span
|
span
|
||||||
.nav-right.nav-menu
|
.nav-right.nav-menu
|
||||||
a.nav-item(href='#')
|
block rootNavRight
|
||||||
| History
|
a.nav-item(href='#')
|
||||||
a.nav-item(href='#')
|
| History
|
||||||
| Source
|
a.nav-item(href='#')
|
||||||
span.nav-item
|
| Source
|
||||||
a.button
|
span.nav-item
|
||||||
span.icon
|
a.button
|
||||||
i.fa.fa-edit
|
span.icon
|
||||||
span Edit
|
i.fa.fa-edit
|
||||||
a.button.is-primary(href='#', onclick='$(".modal").addClass("is-active");')
|
span Edit
|
||||||
span.icon
|
a.button.is-primary(href='#', onclick='$(".modal").addClass("is-active");')
|
||||||
i.fa.fa-plus
|
span.icon
|
||||||
span Create
|
i.fa.fa-plus
|
||||||
|
span Create
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,14 +3,22 @@ html
|
|||||||
head
|
head
|
||||||
meta(http-equiv='X-UA-Compatible', content='IE=edge')
|
meta(http-equiv='X-UA-Compatible', content='IE=edge')
|
||||||
meta(charset='UTF-8')
|
meta(charset='UTF-8')
|
||||||
|
meta(name='viewport', content='width=device-width, initial-scale=1')
|
||||||
|
meta(name='theme-color', content='#009688')
|
||||||
|
meta(name='msapplication-TileColor', content='#009688')
|
||||||
|
meta(name='msapplication-TileImage', content='/favicons/ms-icon-144x144.png')
|
||||||
title= appconfig.title
|
title= appconfig.title
|
||||||
|
|
||||||
// Favicon
|
// Favicon
|
||||||
|
each favsize in [57, 60, 72, 76, 114, 120, 144, 152, 180]
|
||||||
|
link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href='/favicons/apple-icon-' + favsize + 'x' + favsize + '.png')
|
||||||
|
link(rel='icon', type='image/png', sizes='192x192', href='/favicons/android-icon-192x192.png')
|
||||||
each favsize in [32, 96, 16]
|
each favsize in [32, 96, 16]
|
||||||
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize href='/images/favicon-' + favsize + 'x' + favsize + '.png')
|
link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href='/favicons/favicon-' + favsize + 'x' + favsize + '.png')
|
||||||
|
link(rel='manifest', href='/manifest.json')
|
||||||
|
|
||||||
// CSS
|
// CSS
|
||||||
link(href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700|Inconsolata', rel='stylesheet', type='text/css')
|
link(type='text/css', rel='stylesheet', href='/css/libs.css')
|
||||||
link(type='text/css', rel='stylesheet', href='/css/app.css')
|
link(type='text/css', rel='stylesheet', href='/css/app.css')
|
||||||
|
|
||||||
body(class='server-error')
|
body(class='server-error')
|
||||||
|
24
views/pages/edit.pug
Normal file
24
views/pages/edit.pug
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
extends ../layout
|
||||||
|
|
||||||
|
block rootNavCenter
|
||||||
|
h2.nav-item= pageData.meta.title
|
||||||
|
|
||||||
|
block rootNavRight
|
||||||
|
a.nav-item(href='#')
|
||||||
|
| History
|
||||||
|
a.nav-item(href='#')
|
||||||
|
| Source
|
||||||
|
span.nav-item
|
||||||
|
a.button.is-danger(href='/' + pageData.meta.path)
|
||||||
|
span.icon
|
||||||
|
i.fa.fa-times
|
||||||
|
span Discard
|
||||||
|
a.button.is-success(href='#', onclick='$(".modal").addClass("is-active");')
|
||||||
|
span.icon
|
||||||
|
i.fa.fa-check
|
||||||
|
span Save Changes
|
||||||
|
|
||||||
|
block content
|
||||||
|
|
||||||
|
section.section.is-small
|
||||||
|
textarea#mk-editor= pageData.markdown
|
@ -8,6 +8,21 @@ mixin tocMenu(ti)
|
|||||||
ul
|
ul
|
||||||
+tocMenu(node.nodes)
|
+tocMenu(node.nodes)
|
||||||
|
|
||||||
|
block rootNavRight
|
||||||
|
a.nav-item(href='#')
|
||||||
|
| History
|
||||||
|
a.nav-item(href='#')
|
||||||
|
| Source
|
||||||
|
span.nav-item
|
||||||
|
a.button(href='/edit/' + pageData.meta.path)
|
||||||
|
span.icon
|
||||||
|
i.fa.fa-edit
|
||||||
|
span Edit
|
||||||
|
a.button.is-primary(href='#', onclick='$(".modal").addClass("is-active");')
|
||||||
|
span.icon
|
||||||
|
i.fa.fa-plus
|
||||||
|
span Create
|
||||||
|
|
||||||
block content
|
block content
|
||||||
|
|
||||||
section.section
|
section.section
|
||||||
@ -23,8 +38,9 @@ block content
|
|||||||
ul.menu-list
|
ul.menu-list
|
||||||
li
|
li
|
||||||
a(href='/') Home
|
a(href='/') Home
|
||||||
li
|
if pageData.parent
|
||||||
a(href='/') Storage
|
li
|
||||||
|
a(href='/' + pageData.parent.path)= pageData.parent.title
|
||||||
li
|
li
|
||||||
a(href='/account') Account
|
a(href='/account') Account
|
||||||
.box.stickyscroll(data-margin-top=70)
|
.box.stickyscroll(data-margin-top=70)
|
||||||
|
Loading…
Reference in New Issue
Block a user