refactor: Pre-render TeX + MathML server-side to SVG
This commit is contained in:
parent
13d355bd1c
commit
3d9aa18c05
@ -1,5 +0,0 @@
|
|||||||
window.MathJax = {
|
|
||||||
root: '/js/mathjax',
|
|
||||||
delayStartupUntil: 'configured'
|
|
||||||
}
|
|
||||||
;
|
|
@ -65,56 +65,6 @@ module.exports = Promise.mapSeries([
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* MathJax
|
|
||||||
*/
|
|
||||||
() => {
|
|
||||||
return fs.accessAsync('./assets/js/mathjax').then(() => {
|
|
||||||
console.info(colors.white(' └── ') + colors.magenta('MathJax directory already exists. Task aborted.'))
|
|
||||||
return true
|
|
||||||
}).catch(err => {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
console.info(colors.white(' └── ') + colors.green('Copy MathJax dependencies to assets...'))
|
|
||||||
return fs.ensureDirAsync('./assets/js/mathjax').then(() => {
|
|
||||||
return fs.copyAsync('./node_modules/mathjax', './assets/js/mathjax', {
|
|
||||||
filter: (src, dest) => {
|
|
||||||
let srcNormalized = src.replace(/\\/g, '/')
|
|
||||||
let shouldCopy = false
|
|
||||||
console.info(colors.white(' ' + srcNormalized))
|
|
||||||
_.forEach([
|
|
||||||
'/node_modules/mathjax',
|
|
||||||
'/node_modules/mathjax/jax',
|
|
||||||
'/node_modules/mathjax/jax/input',
|
|
||||||
'/node_modules/mathjax/jax/output'
|
|
||||||
], chk => {
|
|
||||||
if (srcNormalized.endsWith(chk)) {
|
|
||||||
shouldCopy = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
_.forEach([
|
|
||||||
'/node_modules/mathjax/extensions',
|
|
||||||
'/node_modules/mathjax/MathJax.js',
|
|
||||||
'/node_modules/mathjax/jax/element',
|
|
||||||
'/node_modules/mathjax/jax/input/MathML',
|
|
||||||
'/node_modules/mathjax/jax/input/TeX',
|
|
||||||
'/node_modules/mathjax/jax/output/SVG'
|
|
||||||
], chk => {
|
|
||||||
if (srcNormalized.indexOf(chk) > 0) {
|
|
||||||
shouldCopy = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (shouldCopy && srcNormalized.indexOf('/fonts/') > 0 && srcNormalized.indexOf('/STIX-Web') <= 1) {
|
|
||||||
shouldCopy = false
|
|
||||||
}
|
|
||||||
return shouldCopy
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* i18n
|
* i18n
|
||||||
*/
|
*/
|
||||||
@ -136,21 +86,6 @@ module.exports = Promise.mapSeries([
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* Bundle pre-init scripts
|
|
||||||
*/
|
|
||||||
() => {
|
|
||||||
console.info(colors.white(' └── ') + colors.green('Bundling pre-init scripts...'))
|
|
||||||
let preInitContent = ''
|
|
||||||
return fs.readdirAsync('./client/js/pre-init').map(f => {
|
|
||||||
let fPath = path.join('./client/js/pre-init/', f)
|
|
||||||
return fs.readFileAsync(fPath, 'utf8').then(fContent => {
|
|
||||||
preInitContent += fContent + ';\n'
|
|
||||||
})
|
|
||||||
}).then(() => {
|
|
||||||
return fs.outputFileAsync('./.build/_preinit.js', preInitContent, 'utf8')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Delete Fusebox cache
|
* Delete Fusebox cache
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/* global $ */
|
/* global $, siteRoot */
|
||||||
/* eslint-disable no-new */
|
/* eslint-disable no-new */
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
@ -64,6 +64,7 @@ import colorPickerComponent from './components/color-picker.vue'
|
|||||||
import editorCodeblockComponent from './components/editor-codeblock.vue'
|
import editorCodeblockComponent from './components/editor-codeblock.vue'
|
||||||
import editorFileComponent from './components/editor-file.vue'
|
import editorFileComponent from './components/editor-file.vue'
|
||||||
import editorVideoComponent from './components/editor-video.vue'
|
import editorVideoComponent from './components/editor-video.vue'
|
||||||
|
import historyComponent from './components/history.vue'
|
||||||
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
import loadingSpinnerComponent from './components/loading-spinner.vue'
|
||||||
import modalCreatePageComponent from './components/modal-create-page.vue'
|
import modalCreatePageComponent from './components/modal-create-page.vue'
|
||||||
import modalCreateUserComponent from './components/modal-create-user.vue'
|
import modalCreateUserComponent from './components/modal-create-user.vue'
|
||||||
@ -130,7 +131,7 @@ i18next
|
|||||||
.use(i18nextXHR)
|
.use(i18nextXHR)
|
||||||
.init({
|
.init({
|
||||||
backend: {
|
backend: {
|
||||||
loadPath: '/js/i18n/{{lng}}.json'
|
loadPath: siteRoot + '/js/i18n/{{lng}}.json'
|
||||||
},
|
},
|
||||||
lng: siteLang,
|
lng: siteLang,
|
||||||
fallbackLng: siteLang
|
fallbackLng: siteLang
|
||||||
@ -176,6 +177,7 @@ $(() => {
|
|||||||
editorCodeblock: editorCodeblockComponent,
|
editorCodeblock: editorCodeblockComponent,
|
||||||
editorFile: editorFileComponent,
|
editorFile: editorFileComponent,
|
||||||
editorVideo: editorVideoComponent,
|
editorVideo: editorVideoComponent,
|
||||||
|
history: historyComponent,
|
||||||
loadingSpinner: loadingSpinnerComponent,
|
loadingSpinner: loadingSpinnerComponent,
|
||||||
modalCreatePage: modalCreatePageComponent,
|
modalCreatePage: modalCreatePageComponent,
|
||||||
modalCreateUser: modalCreateUserComponent,
|
modalCreateUser: modalCreateUserComponent,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/* global $ */
|
/* global $, siteRoot */
|
||||||
|
|
||||||
let mde
|
let mde
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export default {
|
|||||||
return resp.json()
|
return resp.json()
|
||||||
}).then(resp => {
|
}).then(resp => {
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
window.location.assign('/' + self.currentPath)
|
window.location.assign(siteRoot + '/' + self.currentPath)
|
||||||
} else {
|
} else {
|
||||||
self.$store.dispatch('alert', {
|
self.$store.dispatch('alert', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
|
41
client/js/components/history.vue
Normal file
41
client/js/components/history.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
div {{ currentPath }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'history',
|
||||||
|
props: ['currentPath'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tree: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetch(basePath) {
|
||||||
|
let self = this
|
||||||
|
self.$store.dispatch('startLoading')
|
||||||
|
self.$nextTick(() => {
|
||||||
|
socket.emit('treeFetch', { basePath }, (data) => {
|
||||||
|
if (self.tree.length > 0) {
|
||||||
|
let branch = self._.last(self.tree)
|
||||||
|
branch.hasChildren = true
|
||||||
|
self._.find(branch.pages, { _id: basePath }).isActive = true
|
||||||
|
}
|
||||||
|
self.tree.push({
|
||||||
|
hasChildren: false,
|
||||||
|
pages: data
|
||||||
|
})
|
||||||
|
self.$store.dispatch('stopLoading')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goto(entryPath) {
|
||||||
|
window.location.assign(siteRoot + '/' + entryPath)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -10,7 +10,7 @@
|
|||||||
li(v-if='searchres.length === 0')
|
li(v-if='searchres.length === 0')
|
||||||
a: em {{ $t('search.nomatch') }}
|
a: em {{ $t('search.nomatch') }}
|
||||||
li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }')
|
li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }')
|
||||||
a(v-bind:href='"/" + sres.entryPath') {{ sres.title }}
|
a(v-bind:href='siteRoot + "/" + sres.entryPath') {{ sres.title }}
|
||||||
p.searchresults-label(v-if='searchsuggest.length > 0') {{ $t('search.didyoumean') }}
|
p.searchresults-label(v-if='searchsuggest.length > 0') {{ $t('search.didyoumean') }}
|
||||||
ul.searchresults-list(v-if='searchsuggest.length > 0')
|
ul.searchresults-list(v-if='searchsuggest.length > 0')
|
||||||
li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }')
|
li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }')
|
||||||
@ -18,81 +18,81 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
searchq: '',
|
searchq: '',
|
||||||
searchres: [],
|
searchres: [],
|
||||||
searchsuggest: [],
|
searchsuggest: [],
|
||||||
searchload: 0,
|
searchload: 0,
|
||||||
searchactive: false,
|
searchactive: false,
|
||||||
searchmoveidx: 0,
|
searchmoveidx: 0,
|
||||||
searchmovekey: '',
|
searchmovekey: '',
|
||||||
searchmovearr: []
|
searchmovearr: []
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
searchq: function (val, oldVal) {
|
|
||||||
let self = this
|
|
||||||
self.searchmoveidx = 0
|
|
||||||
if (val.length >= 3) {
|
|
||||||
self.searchactive = true
|
|
||||||
self.searchload++
|
|
||||||
socket.emit('search', { terms: val }, (data) => {
|
|
||||||
self.searchres = data.match
|
|
||||||
self.searchsuggest = data.suggest
|
|
||||||
self.searchmovearr = self._.concat([], self.searchres, self.searchsuggest)
|
|
||||||
if (self.searchload > 0) { self.searchload-- }
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
self.searchactive = false
|
|
||||||
self.searchres = []
|
|
||||||
self.searchsuggest = []
|
|
||||||
self.searchmovearr = []
|
|
||||||
self.searchload = 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
searchmoveidx: function (val, oldVal) {
|
|
||||||
if (val > 0) {
|
|
||||||
this.searchmovekey = (this.searchmovearr[val - 1])
|
|
||||||
? 'res.' + this.searchmovearr[val - 1].entryPath
|
|
||||||
: 'sug.' + this.searchmovearr[val - 1]
|
|
||||||
} else {
|
|
||||||
this.searchmovekey = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
useSuggestion: function (sug) {
|
|
||||||
this.searchq = sug
|
|
||||||
},
|
|
||||||
closeSearch: function() {
|
|
||||||
this.searchq = ''
|
|
||||||
},
|
|
||||||
moveSelectSearch: function () {
|
|
||||||
if (this.searchmoveidx < 1) { return }
|
|
||||||
let i = this.searchmoveidx - 1
|
|
||||||
|
|
||||||
if (this.searchmovearr[i]) {
|
|
||||||
window.location.assign('/' + this.searchmovearr[i].entryPath)
|
|
||||||
} else {
|
|
||||||
this.searchq = this.searchmovearr[i]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moveDownSearch: function () {
|
|
||||||
if (this.searchmoveidx < this.searchmovearr.length) {
|
|
||||||
this.searchmoveidx++
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moveUpSearch: function () {
|
|
||||||
if (this.searchmoveidx > 0) {
|
|
||||||
this.searchmoveidx--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted: function () {
|
|
||||||
let self = this
|
|
||||||
$('main').on('click', self.closeSearch)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
searchq: function (val, oldVal) {
|
||||||
|
let self = this
|
||||||
|
self.searchmoveidx = 0
|
||||||
|
if (val.length >= 3) {
|
||||||
|
self.searchactive = true
|
||||||
|
self.searchload++
|
||||||
|
socket.emit('search', { terms: val }, (data) => {
|
||||||
|
self.searchres = data.match
|
||||||
|
self.searchsuggest = data.suggest
|
||||||
|
self.searchmovearr = self._.concat([], self.searchres, self.searchsuggest)
|
||||||
|
if (self.searchload > 0) { self.searchload-- }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self.searchactive = false
|
||||||
|
self.searchres = []
|
||||||
|
self.searchsuggest = []
|
||||||
|
self.searchmovearr = []
|
||||||
|
self.searchload = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchmoveidx: function (val, oldVal) {
|
||||||
|
if (val > 0) {
|
||||||
|
this.searchmovekey = (this.searchmovearr[val - 1])
|
||||||
|
? 'res.' + this.searchmovearr[val - 1].entryPath
|
||||||
|
: 'sug.' + this.searchmovearr[val - 1]
|
||||||
|
} else {
|
||||||
|
this.searchmovekey = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
useSuggestion: function (sug) {
|
||||||
|
this.searchq = sug
|
||||||
|
},
|
||||||
|
closeSearch: function () {
|
||||||
|
this.searchq = ''
|
||||||
|
},
|
||||||
|
moveSelectSearch: function () {
|
||||||
|
if (this.searchmoveidx < 1) { return }
|
||||||
|
let i = this.searchmoveidx - 1
|
||||||
|
|
||||||
|
if (this.searchmovearr[i]) {
|
||||||
|
window.location.assign(siteRoot + '/' + this.searchmovearr[i].entryPath)
|
||||||
|
} else {
|
||||||
|
this.searchq = this.searchmovearr[i]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveDownSearch: function () {
|
||||||
|
if (this.searchmoveidx < this.searchmovearr.length) {
|
||||||
|
this.searchmoveidx++
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveUpSearch: function () {
|
||||||
|
if (this.searchmoveidx > 0) {
|
||||||
|
this.searchmoveidx--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
let self = this
|
||||||
|
$('main').on('click', self.closeSearch)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: '',
|
name: 'tree',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tree: []
|
tree: []
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
/* global $ */
|
/* global $ */
|
||||||
|
|
||||||
import MathJax from 'mathjax'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'content-view',
|
name: 'content-view',
|
||||||
data() {
|
data() {
|
||||||
@ -19,23 +17,5 @@ export default {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
MathJax.Hub.Config({
|
|
||||||
jax: ['input/TeX', 'input/MathML', 'output/SVG'],
|
|
||||||
extensions: ['tex2jax.js', 'mml2jax.js'],
|
|
||||||
TeX: {
|
|
||||||
extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
|
|
||||||
},
|
|
||||||
SVG: {
|
|
||||||
scale: 120,
|
|
||||||
font: 'STIX-Web'
|
|
||||||
},
|
|
||||||
tex2jax: {
|
|
||||||
preview: 'none'
|
|
||||||
},
|
|
||||||
showMathMenu: false,
|
|
||||||
showProcessingMessages: false,
|
|
||||||
messageStyle: 'none'
|
|
||||||
})
|
|
||||||
MathJax.Hub.Configured()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
/* global siteRoot */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'source-view',
|
name: 'source-view',
|
||||||
data() {
|
data() {
|
||||||
@ -7,7 +9,7 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
let self = this
|
let self = this
|
||||||
FuseBox.import('/js/ace/ace.js', (ace) => {
|
FuseBox.import(siteRoot + '/js/ace/ace.js', (ace) => {
|
||||||
let scEditor = ace.edit('source-display')
|
let scEditor = ace.edit('source-display')
|
||||||
scEditor.setTheme('ace/theme/dawn')
|
scEditor.setTheme('ace/theme/dawn')
|
||||||
scEditor.getSession().setMode('ace/mode/markdown')
|
scEditor.getSession().setMode('ace/mode/markdown')
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
window.MathJax = {
|
|
||||||
root: '/js/mathjax',
|
|
||||||
delayStartupUntil: 'configured'
|
|
||||||
}
|
|
@ -134,14 +134,6 @@ git:
|
|||||||
# Whether to use user email as author in commits
|
# Whether to use user email as author in commits
|
||||||
showUserEmail: true
|
showUserEmail: true
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# Features
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# You can enable / disable specific features below
|
|
||||||
|
|
||||||
features:
|
|
||||||
mathjax: true
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# External Logging
|
# External Logging
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
8
fuse.js
8
fuse.js
@ -53,17 +53,9 @@ const ALIASES = {
|
|||||||
'vue-lodash': 'vue-lodash/dist/vue-lodash.min.js'
|
'vue-lodash': 'vue-lodash/dist/vue-lodash.min.js'
|
||||||
}
|
}
|
||||||
const SHIMS = {
|
const SHIMS = {
|
||||||
_preinit: {
|
|
||||||
source: '.build/_preinit.js',
|
|
||||||
exports: '_preinit'
|
|
||||||
},
|
|
||||||
jquery: {
|
jquery: {
|
||||||
source: 'node_modules/jquery/dist/jquery.js',
|
source: 'node_modules/jquery/dist/jquery.js',
|
||||||
exports: '$'
|
exports: '$'
|
||||||
},
|
|
||||||
mathjax: {
|
|
||||||
source: 'node_modules/mathjax/MathJax.js',
|
|
||||||
exports: 'MathJax'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,14 +132,6 @@ git:
|
|||||||
# Whether to use user email as author in commits
|
# Whether to use user email as author in commits
|
||||||
showUserEmail: true
|
showUserEmail: true
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# Features
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# You can enable / disable specific features below
|
|
||||||
|
|
||||||
features:
|
|
||||||
mathjax: true
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# External Logging
|
# External Logging
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
@ -132,14 +132,6 @@ git:
|
|||||||
# Whether to use user email as author in commits
|
# Whether to use user email as author in commits
|
||||||
showUserEmail: $(WIKI_SHOW_USER_EMAIL)
|
showUserEmail: $(WIKI_SHOW_USER_EMAIL)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# Features
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# You can enable / disable specific features below
|
|
||||||
|
|
||||||
features:
|
|
||||||
mathjax: true
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# External Logging
|
# External Logging
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
15
package.json
15
package.json
@ -85,6 +85,7 @@
|
|||||||
"markdown-it-footnote": "^3.0.1",
|
"markdown-it-footnote": "^3.0.1",
|
||||||
"markdown-it-mathjax": "^2.0.0",
|
"markdown-it-mathjax": "^2.0.0",
|
||||||
"markdown-it-task-lists": "^2.0.1",
|
"markdown-it-task-lists": "^2.0.1",
|
||||||
|
"mathjax-node": "^1.1.0",
|
||||||
"memdown": "^1.2.4",
|
"memdown": "^1.2.4",
|
||||||
"mime-types": "^2.1.15",
|
"mime-types": "^2.1.15",
|
||||||
"moment": "^2.18.1",
|
"moment": "^2.18.1",
|
||||||
@ -100,7 +101,7 @@
|
|||||||
"passport-facebook": "^2.1.1",
|
"passport-facebook": "^2.1.1",
|
||||||
"passport-github2": "^0.1.10",
|
"passport-github2": "^0.1.10",
|
||||||
"passport-google-oauth20": "^1.0.0",
|
"passport-google-oauth20": "^1.0.0",
|
||||||
"passport-ldapauth": "^1.0.0",
|
"passport-ldapauth": "^2.0.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"passport-slack": "0.0.7",
|
"passport-slack": "0.0.7",
|
||||||
"passport-windowslive": "^1.0.2",
|
"passport-windowslive": "^1.0.2",
|
||||||
@ -134,9 +135,9 @@
|
|||||||
"brace": "^0.10.0",
|
"brace": "^0.10.0",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"consolidate": "^0.14.5",
|
"consolidate": "^0.14.5",
|
||||||
"eslint": "^3.19.0",
|
"eslint": "^4.0.0",
|
||||||
"eslint-config-standard": "^10.2.1",
|
"eslint-config-standard": "^10.2.1",
|
||||||
"eslint-plugin-import": "^2.3.0",
|
"eslint-plugin-import": "^2.6.0",
|
||||||
"eslint-plugin-node": "^5.0.0",
|
"eslint-plugin-node": "^5.0.0",
|
||||||
"eslint-plugin-promise": "^3.5.0",
|
"eslint-plugin-promise": "^3.5.0",
|
||||||
"eslint-plugin-standard": "^3.0.1",
|
"eslint-plugin-standard": "^3.0.1",
|
||||||
@ -154,12 +155,12 @@
|
|||||||
"node-sass": "^4.5.3",
|
"node-sass": "^4.5.3",
|
||||||
"nodemon": "^1.11.0",
|
"nodemon": "^1.11.0",
|
||||||
"pug-lint": "^2.4.0",
|
"pug-lint": "^2.4.0",
|
||||||
"snyk": "^1.34.3",
|
"snyk": "^1.36.0",
|
||||||
"twemoji-awesome": "^1.0.6",
|
"twemoji-awesome": "^1.0.6",
|
||||||
"typescript": "^2.3.4",
|
"typescript": "^2.3.4",
|
||||||
"uglify-es": "^3.0.15",
|
"uglify-es": "^3.0.19",
|
||||||
"uglify-js": "^3.0.15",
|
"uglify-js": "^3.0.19",
|
||||||
"vee-validate": "^2.0.0-rc.5",
|
"vee-validate": "^2.0.0-rc.6",
|
||||||
"vue": "^2.3.4",
|
"vue": "^2.3.4",
|
||||||
"vue-clipboards": "^1.0.2",
|
"vue-clipboards": "^1.0.2",
|
||||||
"vue-lodash": "^1.0.3",
|
"vue-lodash": "^1.0.3",
|
||||||
|
@ -22,7 +22,7 @@ module.exports = {
|
|||||||
*
|
*
|
||||||
* @return {Object} Entries model instance
|
* @return {Object} Entries model instance
|
||||||
*/
|
*/
|
||||||
init () {
|
init() {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
self._repoPath = path.resolve(ROOTPATH, appconfig.paths.repo)
|
self._repoPath = path.resolve(ROOTPATH, appconfig.paths.repo)
|
||||||
@ -39,7 +39,7 @@ module.exports = {
|
|||||||
* @param {String} entryPath The entry path
|
* @param {String} entryPath The entry path
|
||||||
* @return {Promise<Boolean>} True if exists, false otherwise
|
* @return {Promise<Boolean>} True if exists, false otherwise
|
||||||
*/
|
*/
|
||||||
exists (entryPath) {
|
exists(entryPath) {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
return self.fetchOriginal(entryPath, {
|
return self.fetchOriginal(entryPath, {
|
||||||
@ -62,7 +62,7 @@ module.exports = {
|
|||||||
* @param {String} entryPath The entry path
|
* @param {String} entryPath The entry path
|
||||||
* @return {Promise<Object>} Page Data
|
* @return {Promise<Object>} Page Data
|
||||||
*/
|
*/
|
||||||
fetch (entryPath) {
|
fetch(entryPath) {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
let cpath = entryHelper.getCachePath(entryPath)
|
let cpath = entryHelper.getCachePath(entryPath)
|
||||||
@ -97,7 +97,7 @@ module.exports = {
|
|||||||
* @param {Object} options The options
|
* @param {Object} options The options
|
||||||
* @return {Promise<Object>} Page data
|
* @return {Promise<Object>} Page data
|
||||||
*/
|
*/
|
||||||
fetchOriginal (entryPath, options) {
|
fetchOriginal(entryPath, options) {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
let fpath = entryHelper.getFullPath(entryPath)
|
let fpath = entryHelper.getFullPath(entryPath)
|
||||||
@ -115,43 +115,47 @@ module.exports = {
|
|||||||
return fs.statAsync(fpath).then((st) => {
|
return fs.statAsync(fpath).then((st) => {
|
||||||
if (st.isFile()) {
|
if (st.isFile()) {
|
||||||
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
|
return fs.readFileAsync(fpath, 'utf8').then((contents) => {
|
||||||
|
let htmlProcessor = (options.parseMarkdown) ? mark.parseContent(contents) : Promise.resolve('')
|
||||||
|
|
||||||
// Parse contents
|
// Parse contents
|
||||||
|
|
||||||
let pageData = {
|
return htmlProcessor.then(html => {
|
||||||
markdown: (options.includeMarkdown) ? contents : '',
|
let pageData = {
|
||||||
html: (options.parseMarkdown) ? mark.parseContent(contents) : '',
|
markdown: (options.includeMarkdown) ? contents : '',
|
||||||
meta: (options.parseMeta) ? mark.parseMeta(contents) : {},
|
html,
|
||||||
tree: (options.parseTree) ? mark.parseTree(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) => { // eslint-disable-line handle-callback-err
|
|
||||||
return (pageData.parent = false)
|
|
||||||
}) : Promise.resolve(true)
|
|
||||||
|
|
||||||
return parentPromise.then(() => {
|
|
||||||
// Cache to disk
|
|
||||||
|
|
||||||
if (options.cache) {
|
|
||||||
let cacheData = JSON.stringify(_.pick(pageData, ['html', 'meta', 'tree', 'parent']), false, false, false)
|
|
||||||
return fs.writeFileAsync(cpath, cacheData).catch((err) => {
|
|
||||||
winston.error('Unable to write to cache! Performance may be affected.')
|
|
||||||
winston.error(err)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}).return(pageData)
|
|
||||||
|
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) => { // eslint-disable-line handle-callback-err
|
||||||
|
return (pageData.parent = false)
|
||||||
|
}) : Promise.resolve(true)
|
||||||
|
|
||||||
|
return parentPromise.then(() => {
|
||||||
|
// Cache to disk
|
||||||
|
|
||||||
|
if (options.cache) {
|
||||||
|
let cacheData = JSON.stringify(_.pick(pageData, ['html', 'meta', 'tree', 'parent']), false, false, false)
|
||||||
|
return fs.writeFileAsync(cpath, cacheData).catch((err) => {
|
||||||
|
winston.error('Unable to write to cache! Performance may be affected.')
|
||||||
|
winston.error(err)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}).return(pageData)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -167,7 +171,7 @@ module.exports = {
|
|||||||
* @param {String} entryPath The entry path
|
* @param {String} entryPath The entry path
|
||||||
* @return {Promise<Object|False>} The parent information.
|
* @return {Promise<Object|False>} The parent information.
|
||||||
*/
|
*/
|
||||||
getParentInfo (entryPath) {
|
getParentInfo(entryPath) {
|
||||||
if (_.includes(entryPath, '/')) {
|
if (_.includes(entryPath, '/')) {
|
||||||
let parentParts = _.initial(_.split(entryPath, '/'))
|
let parentParts = _.initial(_.split(entryPath, '/'))
|
||||||
let parentPath = _.join(parentParts, '/')
|
let parentPath = _.join(parentParts, '/')
|
||||||
@ -202,7 +206,7 @@ module.exports = {
|
|||||||
* @param {Object} author The author user object
|
* @param {Object} author The author user object
|
||||||
* @return {Promise<Boolean>} True on success, false on failure
|
* @return {Promise<Boolean>} True on success, false on failure
|
||||||
*/
|
*/
|
||||||
update (entryPath, contents, author) {
|
update(entryPath, contents, author) {
|
||||||
let self = this
|
let self = this
|
||||||
let fpath = entryHelper.getFullPath(entryPath)
|
let fpath = entryHelper.getFullPath(entryPath)
|
||||||
|
|
||||||
@ -228,7 +232,7 @@ module.exports = {
|
|||||||
* @param {String} entryPath The entry path
|
* @param {String} entryPath The entry path
|
||||||
* @return {Promise} Promise of the operation
|
* @return {Promise} Promise of the operation
|
||||||
*/
|
*/
|
||||||
updateCache (entryPath) {
|
updateCache(entryPath) {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
return self.fetchOriginal(entryPath, {
|
return self.fetchOriginal(entryPath, {
|
||||||
@ -256,21 +260,21 @@ module.exports = {
|
|||||||
return db.Entry.findOneAndUpdate({
|
return db.Entry.findOneAndUpdate({
|
||||||
_id: content.entryPath
|
_id: content.entryPath
|
||||||
}, {
|
}, {
|
||||||
_id: content.entryPath,
|
_id: content.entryPath,
|
||||||
title: content.meta.title || content.entryPath,
|
title: content.meta.title || content.entryPath,
|
||||||
subtitle: content.meta.subtitle || '',
|
subtitle: content.meta.subtitle || '',
|
||||||
parentTitle: content.parent.title || '',
|
parentTitle: content.parent.title || '',
|
||||||
parentPath: parentPath,
|
parentPath: parentPath,
|
||||||
isDirectory: false,
|
isDirectory: false,
|
||||||
isEntry: true
|
isEntry: true
|
||||||
}, {
|
}, {
|
||||||
new: true,
|
new: true,
|
||||||
upsert: true
|
upsert: true
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
let plainResult = result.toObject()
|
let plainResult = result.toObject()
|
||||||
plainResult.text = content.text
|
plainResult.text = content.text
|
||||||
return plainResult
|
return plainResult
|
||||||
})
|
})
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
return self.updateTreeInfo().then(() => {
|
return self.updateTreeInfo().then(() => {
|
||||||
return result
|
return result
|
||||||
@ -286,7 +290,7 @@ module.exports = {
|
|||||||
*
|
*
|
||||||
* @returns {Promise<Boolean>} Promise of the operation
|
* @returns {Promise<Boolean>} Promise of the operation
|
||||||
*/
|
*/
|
||||||
updateTreeInfo () {
|
updateTreeInfo() {
|
||||||
return db.Entry.distinct('parentPath', { parentPath: { $ne: '' } }).then(allPaths => {
|
return db.Entry.distinct('parentPath', { parentPath: { $ne: '' } }).then(allPaths => {
|
||||||
if (allPaths.length > 0) {
|
if (allPaths.length > 0) {
|
||||||
return Promise.map(allPaths, pathItem => {
|
return Promise.map(allPaths, pathItem => {
|
||||||
@ -311,7 +315,7 @@ module.exports = {
|
|||||||
* @param {Object} author The author user object
|
* @param {Object} author The author user object
|
||||||
* @return {Promise<Boolean>} True on success, false on failure
|
* @return {Promise<Boolean>} True on success, false on failure
|
||||||
*/
|
*/
|
||||||
create (entryPath, contents, author) {
|
create(entryPath, contents, author) {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
return self.exists(entryPath).then((docExists) => {
|
return self.exists(entryPath).then((docExists) => {
|
||||||
@ -338,7 +342,7 @@ module.exports = {
|
|||||||
* @param {Object} author The author user object
|
* @param {Object} author The author user object
|
||||||
* @return {Promise<Boolean>} True on success, false on failure
|
* @return {Promise<Boolean>} True on success, false on failure
|
||||||
*/
|
*/
|
||||||
makePersistent (entryPath, contents, author) {
|
makePersistent(entryPath, contents, author) {
|
||||||
let fpath = entryHelper.getFullPath(entryPath)
|
let fpath = entryHelper.getFullPath(entryPath)
|
||||||
|
|
||||||
return fs.outputFileAsync(fpath, contents).then(() => {
|
return fs.outputFileAsync(fpath, contents).then(() => {
|
||||||
@ -354,7 +358,7 @@ module.exports = {
|
|||||||
* @param {Object} author The author user object
|
* @param {Object} author The author user object
|
||||||
* @return {Promise} Promise of the operation
|
* @return {Promise} Promise of the operation
|
||||||
*/
|
*/
|
||||||
move (entryPath, newEntryPath, author) {
|
move(entryPath, newEntryPath, author) {
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
if (_.isEmpty(entryPath) || entryPath === 'home') {
|
if (_.isEmpty(entryPath) || entryPath === 'home') {
|
||||||
@ -387,7 +391,7 @@ module.exports = {
|
|||||||
* @param {String} entryPath The entry path
|
* @param {String} entryPath The entry path
|
||||||
* @return {Promise<String>} Starter content
|
* @return {Promise<String>} Starter content
|
||||||
*/
|
*/
|
||||||
getStarter (entryPath) {
|
getStarter(entryPath) {
|
||||||
let formattedTitle = _.startCase(_.last(_.split(entryPath, '/')))
|
let formattedTitle = _.startCase(_.last(_.split(entryPath, '/')))
|
||||||
|
|
||||||
return fs.readFileAsync(path.join(SERVERPATH, 'app/content/create.md'), 'utf8').then((contents) => {
|
return fs.readFileAsync(path.join(SERVERPATH, 'app/content/create.md'), 'utf8').then((contents) => {
|
||||||
@ -402,7 +406,7 @@ module.exports = {
|
|||||||
* @param {Object} usr Current user
|
* @param {Object} usr Current user
|
||||||
* @return {Promise<Array>} List of entries
|
* @return {Promise<Array>} List of entries
|
||||||
*/
|
*/
|
||||||
getFromTree (basePath, usr) {
|
getFromTree(basePath, usr) {
|
||||||
return db.Entry.find({ parentPath: basePath }, 'title parentPath isDirectory isEntry').sort({ title: 'asc' }).then(results => {
|
return db.Entry.find({ parentPath: basePath }, 'title parentPath isDirectory isEntry').sort({ title: 'asc' }).then(results => {
|
||||||
return _.filter(results, r => {
|
return _.filter(results, r => {
|
||||||
return rights.checkRole('/' + r._id, usr.rights, 'read')
|
return rights.checkRole('/' + r._id, usr.rights, 'read')
|
||||||
@ -410,7 +414,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getHistory (entryPath) {
|
getHistory(entryPath) {
|
||||||
return db.Entry.findOne({ _id: entryPath, isEntry: true }).then(entry => {
|
return db.Entry.findOne({ _id: entryPath, isEntry: true }).then(entry => {
|
||||||
if (!entry) { return false }
|
if (!entry) { return false }
|
||||||
return git.getHistory(entryPath).then(history => {
|
return git.getHistory(entryPath).then(history => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const Promise = require('bluebird')
|
||||||
const md = require('markdown-it')
|
const md = require('markdown-it')
|
||||||
const mdEmoji = require('markdown-it-emoji')
|
const mdEmoji = require('markdown-it-emoji')
|
||||||
const mdTaskLists = require('markdown-it-task-lists')
|
const mdTaskLists = require('markdown-it-task-lists')
|
||||||
@ -9,6 +10,8 @@ const mdFootnote = require('markdown-it-footnote')
|
|||||||
const mdExternalLinks = require('markdown-it-external-links')
|
const mdExternalLinks = require('markdown-it-external-links')
|
||||||
const mdExpandTabs = require('markdown-it-expand-tabs')
|
const mdExpandTabs = require('markdown-it-expand-tabs')
|
||||||
const mdAttrs = require('markdown-it-attrs')
|
const mdAttrs = require('markdown-it-attrs')
|
||||||
|
const mdMathjax = require('markdown-it-mathjax')()
|
||||||
|
const mathjax = require('mathjax-node')
|
||||||
const hljs = require('highlight.js')
|
const hljs = require('highlight.js')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
@ -50,11 +53,7 @@ var mkdown = md({
|
|||||||
tabWidth: 4
|
tabWidth: 4
|
||||||
})
|
})
|
||||||
.use(mdAttrs)
|
.use(mdAttrs)
|
||||||
|
.use(mdMathjax)
|
||||||
if (appconfig) {
|
|
||||||
const mdMathjax = require('markdown-it-mathjax')
|
|
||||||
mkdown.use(mdMathjax())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rendering rules
|
// Rendering rules
|
||||||
|
|
||||||
@ -87,9 +86,40 @@ const videoRules = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// Non-markdown filter
|
// Regex
|
||||||
|
|
||||||
const textRegex = new RegExp('\\b[a-z0-9-.,' + appdata.regex.cjk + appdata.regex.arabic + ']+\\b', 'g')
|
const textRegex = new RegExp('\\b[a-z0-9-.,' + appdata.regex.cjk + appdata.regex.arabic + ']+\\b', 'g')
|
||||||
|
const mathRegex = [
|
||||||
|
{
|
||||||
|
format: 'TeX',
|
||||||
|
regex: /\\\[([\s\S]*?)\\\]/g
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: 'inline-TeX',
|
||||||
|
regex: /\\\((.*?)\\\)/g
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: 'MathML',
|
||||||
|
regex: /<math([\s\S]*?)<\/math>/g
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// MathJax
|
||||||
|
|
||||||
|
mathjax.config({
|
||||||
|
MathJax: {
|
||||||
|
jax: ['input/TeX', 'input/MathML', 'output/SVG'],
|
||||||
|
extensions: ['tex2jax.js', 'mml2jax.js'],
|
||||||
|
TeX: {
|
||||||
|
extensions: ['AMSmath.js', 'AMSsymbols.js', 'noErrors.js', 'noUndefined.js']
|
||||||
|
},
|
||||||
|
SVG: {
|
||||||
|
scale: 120,
|
||||||
|
font: 'STIX-Web'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mathjax.start()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse markdown content and build TOC tree
|
* Parse markdown content and build TOC tree
|
||||||
@ -177,11 +207,10 @@ const parseTree = (content) => {
|
|||||||
* Parse markdown content to HTML
|
* Parse markdown content to HTML
|
||||||
*
|
*
|
||||||
* @param {String} content Markdown content
|
* @param {String} content Markdown content
|
||||||
* @return {String} HTML formatted content
|
* @return {Promise<String>} Promise
|
||||||
*/
|
*/
|
||||||
const parseContent = (content) => {
|
const parseContent = (content) => {
|
||||||
let output = mkdown.render(content)
|
let cr = cheerio.load(mkdown.render(content))
|
||||||
let cr = cheerio.load(output)
|
|
||||||
|
|
||||||
if (cr.root().children().length < 1) {
|
if (cr.root().children().length < 1) {
|
||||||
return ''
|
return ''
|
||||||
@ -265,9 +294,55 @@ const parseContent = (content) => {
|
|||||||
cr(elm).removeClass('align-center')
|
cr(elm).removeClass('align-center')
|
||||||
})
|
})
|
||||||
|
|
||||||
output = cr.html()
|
// Mathjax Post-processor
|
||||||
|
|
||||||
return output
|
return processMathjax(cr.html())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process MathJax expressions
|
||||||
|
*
|
||||||
|
* @param {String} content HTML content
|
||||||
|
* @returns {Promise<String>} Promise
|
||||||
|
*/
|
||||||
|
const processMathjax = (content) => {
|
||||||
|
let matchStack = []
|
||||||
|
let replaceStack = []
|
||||||
|
let currentMatch
|
||||||
|
let mathjaxState = {}
|
||||||
|
|
||||||
|
_.forEach(mathRegex, mode => {
|
||||||
|
do {
|
||||||
|
currentMatch = mode.regex.exec(content)
|
||||||
|
if (currentMatch) {
|
||||||
|
matchStack.push(currentMatch[0])
|
||||||
|
replaceStack.push(
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
mathjax.typeset({
|
||||||
|
math: (mode.format === 'MathML') ? currentMatch[0] : currentMatch[1],
|
||||||
|
format: mode.format,
|
||||||
|
speakText: false,
|
||||||
|
svg: true,
|
||||||
|
state: mathjaxState
|
||||||
|
}, result => {
|
||||||
|
if (!result.errors) {
|
||||||
|
resolve(result.svg)
|
||||||
|
} else {
|
||||||
|
reject(new Error(result.errors.join(', ')))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} while (currentMatch)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (matchStack.length > 0) ? Promise.all(replaceStack).then(results => {
|
||||||
|
_.forEach(matchStack, (repMatch, idx) => {
|
||||||
|
content = content.replace(repMatch, results[idx])
|
||||||
|
})
|
||||||
|
return content
|
||||||
|
}) : Promise.resolve(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,11 +389,13 @@ module.exports = {
|
|||||||
* @return {Object} Object containing meta, html and tree data
|
* @return {Object} Object containing meta, html and tree data
|
||||||
*/
|
*/
|
||||||
parse(content) {
|
parse(content) {
|
||||||
return {
|
return parseContent(content).then(html => {
|
||||||
meta: parseMeta(content),
|
return {
|
||||||
html: parseContent(content),
|
meta: parseMeta(content),
|
||||||
tree: parseTree(content)
|
html,
|
||||||
}
|
tree: parseTree(content)
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
parseContent,
|
parseContent,
|
||||||
|
@ -4,32 +4,24 @@ block rootNavRight
|
|||||||
i.nav-item#notifload
|
i.nav-item#notifload
|
||||||
.nav-item
|
.nav-item
|
||||||
a.button(href='/' + pageData.meta._id)
|
a.button(href='/' + pageData.meta._id)
|
||||||
i.icon-circle-check
|
i.nc-icon-outline.ui-3_select
|
||||||
span= t('nav.viewlatest')
|
span= t('nav.viewlatest')
|
||||||
|
|
||||||
block content
|
block content
|
||||||
|
.container.is-fluid
|
||||||
|
.columns.is-gapless
|
||||||
|
|
||||||
#page-type-history.page-type-container(data-entrypath=pageData.meta._id)
|
.column.is-narrow.is-hidden-touch.sidebar
|
||||||
.container.is-fluid.has-mkcontent
|
|
||||||
.columns.is-gapless
|
|
||||||
|
|
||||||
.column.is-narrow.is-hidden-touch.sidebar
|
aside.stickyscroll
|
||||||
|
.sidebar-label
|
||||||
|
span= t('sidebar.pastversions')
|
||||||
|
ul.sidebar-menu
|
||||||
|
each item, index in pageData.history
|
||||||
|
- var itemDate = moment(item.date)
|
||||||
|
li: a.is-multiline(class={ 'is-active': index < 1 }, href='', title=itemDate.format('LLLL'))
|
||||||
|
span= itemDate.calendar(null, { sameElse: 'llll'})
|
||||||
|
span.is-small= item.commitAbbr
|
||||||
|
|
||||||
aside.stickyscroll
|
.column
|
||||||
.sidebar-label
|
history(current-path=pageData.meta._id)
|
||||||
span= t('sidebar.pastversions')
|
|
||||||
ul.sidebar-menu
|
|
||||||
each item, index in pageData.history
|
|
||||||
- var itemDate = moment(item.date)
|
|
||||||
li: a.is-multiline(class={ 'is-active': index < 1 }, href='', title=itemDate.format('LLLL'))
|
|
||||||
span= itemDate.calendar(null, { sameElse: 'llll'})
|
|
||||||
span.is-small= item.commitAbbr
|
|
||||||
|
|
||||||
.column
|
|
||||||
|
|
||||||
.hero
|
|
||||||
h1.title#title= pageData.meta.title
|
|
||||||
if pageData.meta.subtitle
|
|
||||||
h2.subtitle= pageData.meta.subtitle
|
|
||||||
.content.mkcontent
|
|
||||||
!= pageData.html
|
|
||||||
|
@ -18,8 +18,8 @@ block rootNavRight
|
|||||||
a.button.is-outlined(href='/source/' + pageData.meta.path)
|
a.button.is-outlined(href='/source/' + pageData.meta.path)
|
||||||
i.nc-icon-outline.education_paper
|
i.nc-icon-outline.education_paper
|
||||||
span= t('nav.source')
|
span= t('nav.source')
|
||||||
//- a.button.is-outlined(href='/hist/' + pageData.meta.path)
|
a.button.is-outlined(href='/hist/' + pageData.meta.path)
|
||||||
i.icon-clock
|
i.nc-icon-outline.ui-2_time
|
||||||
span= t('nav.history')
|
span= t('nav.history')
|
||||||
if rights.write
|
if rights.write
|
||||||
a.button(href='/edit/' + pageData.meta.path)
|
a.button(href='/edit/' + pageData.meta.path)
|
||||||
|
Loading…
Reference in New Issue
Block a user