Bläddra i källkod

refactor: editor codeblock -> Vue component

NGPixel 8 år sedan
förälder
incheckning
f577a8134e

+ 161 - 0
.build/_tasks.js

@@ -0,0 +1,161 @@
+'use strict'
+
+const _ = require('lodash')
+const Promise = require('bluebird')
+const colors = require('colors/safe')
+const fs = Promise.promisifyAll(require('fs-extra'))
+const path = require('path')
+const uglify = require('uglify-es')
+
+module.exports = Promise.mapSeries([
+  /**
+   * SimpleMDE
+   */
+  () => {
+    return fs.accessAsync('./assets/js/simplemde').then(() => {
+      console.info(colors.white('  └── ') + colors.magenta('SimpleMDE directory already exists. Task aborted.'))
+      return true
+    }).catch(err => {
+      if (err.code === 'ENOENT') {
+        console.info(colors.white('  └── ') + colors.green('Copy + Minify SimpleMDE to assets...'))
+        return fs.copy('./node_modules/simplemde/dist/simplemde.min.js', './assets/js/simplemde/simplemde.min.js')
+      } else {
+        throw err
+      }
+    })
+  },
+  /**
+   * ACE Modes
+   */
+  () => {
+    return fs.accessAsync('./assets/js/ace').then(() => {
+      console.info(colors.white('  └── ') + colors.magenta('ACE modes directory already exists. Task aborted.'))
+      return true
+    }).catch(err => {
+      if (err.code === 'ENOENT') {
+        console.info(colors.white('  └── ') + colors.green('Copy + Minify ACE modes to assets...'))
+        return fs.ensureDirAsync('./assets/js/ace').then(() => {
+          return Promise.join(
+            // Core
+            Promise.all([
+              fs.readFileAsync('./node_modules/brace/index.js', 'utf8'),
+              fs.readFileAsync('./node_modules/brace/ext/modelist.js', 'utf8'),
+              fs.readFileAsync('./node_modules/brace/theme/dawn.js', 'utf8'),
+              fs.readFileAsync('./node_modules/brace/theme/tomorrow_night.js', 'utf8'),
+              fs.readFileAsync('./node_modules/brace/mode/markdown.js', 'utf8')
+            ]).then(items => {
+              console.info(colors.white('      ace.js'))
+              let result = uglify.minify(items.join(';\n'), { output: { 'max_line_len': 1000000 } })
+              return fs.writeFileAsync('./assets/js/ace/ace.js', result.code)
+            }),
+            // Modes
+            fs.readdirAsync('./node_modules/brace/mode').then(modeList => {
+              return Promise.map(modeList, mdFile => {
+                return fs.readFileAsync(path.join('./node_modules/brace/mode', mdFile), 'utf8').then(modeCode => {
+                  console.info(colors.white('      mode-' + mdFile))
+                  let result = uglify.minify(modeCode, { output: { 'max_line_len': 1000000 } })
+                  return fs.writeFileAsync(path.join('./assets/js/ace', 'mode-' + mdFile), result.code)
+                })
+              }, { concurrency: 3 })
+            })
+          )
+        })
+      } else {
+        throw err
+      }
+    })
+  },
+  /**
+   * 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
+   */
+  () => {
+    console.info(colors.white('  └── ') + colors.green('Copying i18n client files...'))
+    return fs.ensureDirAsync('./assets/js/i18n').then(() => {
+      return fs.readJsonAsync('./server/locales/en/browser.json').then(enContent => {
+        return fs.readdirAsync('./server/locales').then(langs => {
+          return Promise.map(langs, lang => {
+            console.info(colors.white('      ' + lang + '.json'))
+            let outputPath = path.join('./assets/js/i18n', lang + '.json')
+            return fs.readJsonAsync(path.join('./server/locales', lang + '.json'), 'utf8').then((content) => {
+              return fs.outputJsonAsync(outputPath, _.defaultsDeep(content, enContent))
+            }).catch(err => { // eslint-disable-line handle-callback-err
+              return fs.outputJsonAsync(outputPath, enContent)
+            })
+          })
+        })
+      })
+    })
+  },
+  /**
+   * 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
+   */
+  () => {
+    console.info(colors.white('  └── ') + colors.green('Clearing fuse-box cache...'))
+    return fs.emptyDirAsync('./.fusebox')
+  }
+], f => { return f() })

+ 6 - 0
.eslintrc.json

@@ -6,9 +6,15 @@
     "jest": true
   },
   "globals": {
+    // Client
     "document": false,
     "navigator": false,
     "window": false,
+    "siteLang": false,
+    "socket": true,
+    "wikijs": true,
+    "FuseBox": false,
+    // Server
     "appconfig": true,
     "appdata": true,
     "ROOTPATH": true,

+ 1 - 2
client/js/app.js

@@ -1,6 +1,5 @@
 'use strict'
 
-/* global siteLang */
 /* eslint-disable no-new */
 
 import $ from 'jquery'
@@ -147,7 +146,7 @@ $(() => {
   // ====================================
 
   const i18n = new VueI18Next(i18next)
-  new Vue({
+  window.wikijs = new Vue({
     mixins: [helpers],
     components: {
       alert: alertComponent,

+ 0 - 87
client/js/components/editor-codeblock.js

@@ -1,87 +0,0 @@
-'use strict'
-
-import $ from 'jquery'
-import Vue from 'vue'
-import _ from 'lodash'
-import * as ace from 'brace'
-import 'brace/theme/tomorrow_night'
-import 'brace/mode/markdown'
-import 'brace-ext-modelist'
-
-let codeEditor = null
-
-// ACE - Mode Loader
-
-let modelistLoaded = []
-let loadAceMode = (m) => {
-  return $.ajax({
-    url: '/js/ace/mode-' + m + '.js',
-    dataType: 'script',
-    cache: true,
-    beforeSend: () => {
-      if (_.includes(modelistLoaded, m)) {
-        return false
-      }
-    },
-    success: () => {
-      modelistLoaded.push(m)
-    }
-  })
-}
-
-// Vue Code Block instance
-
-module.exports = (mde, mdeModalOpenState) => {
-  let modelist = ace.acequire('ace/ext/modelist')
-  let vueCodeBlock = new Vue({
-    el: '#modal-editor-codeblock',
-    data: {
-      modes: modelist.modesByName,
-      modeSelected: 'text',
-      initContent: ''
-    },
-    watch: {
-      modeSelected: (val, oldVal) => {
-        loadAceMode(val).done(() => {
-          ace.acequire('ace/mode/' + val)
-          codeEditor.getSession().setMode('ace/mode/' + val)
-        })
-      }
-    },
-    methods: {
-      open: (ev) => {
-        mdeModalOpenState = true
-        $('#modal-editor-codeblock').addClass('is-active')
-
-        _.delay(() => {
-          codeEditor = ace.edit('codeblock-editor')
-          codeEditor.setTheme('ace/theme/tomorrow_night')
-          codeEditor.getSession().setMode('ace/mode/' + vueCodeBlock.modeSelected)
-          codeEditor.setOption('fontSize', '14px')
-          codeEditor.setOption('hScrollBarAlwaysVisible', false)
-          codeEditor.setOption('wrap', true)
-
-          codeEditor.setValue(vueCodeBlock.initContent)
-
-          codeEditor.focus()
-          codeEditor.renderer.updateFull()
-        }, 300)
-      },
-      cancel: (ev) => {
-        mdeModalOpenState = false  // eslint-disable-line no-undef
-        $('#modal-editor-codeblock').removeClass('is-active')
-        vueCodeBlock.initContent = ''
-      },
-      insertCode: (ev) => {
-        if (mde.codemirror.doc.somethingSelected()) {
-          mde.codemirror.execCommand('singleSelection')
-        }
-        let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'
-
-        mde.codemirror.doc.replaceSelection(codeBlockText)
-        vueCodeBlock.cancel()
-      }
-    }
-  })
-  return vueCodeBlock
-}

+ 60 - 3
client/js/components/editor-codeblock.vue

@@ -24,28 +24,85 @@
 </template>
 
 <script>
+  let codeEditor
+  let ace
+
   export default {
     name: 'editor-codeblock',
     data () {
-      return {}
+      return {
+        modes: [],
+        modeSelected: 'text',
+        modelistLoaded: []
+      }
     },
     computed: {
+      content () {
+        return this.$store.state.editorCodeblock.content
+      },
       isShown () {
         return this.$store.state.editorCodeblock.shown
       }
     },
+    watch: {
+      modeSelected(val, oldVal) {
+        this.loadMode(val)
+      }
+    },
     methods: {
+      init () {
+        let self = this
+        self._.delay(() => {
+          codeEditor = ace.edit('codeblock-editor')
+          codeEditor.setTheme('ace/theme/tomorrow_night')
+          codeEditor.getSession().setMode('ace/mode/' + self.modeSelected)
+          codeEditor.setOption('fontSize', '14px')
+          codeEditor.setOption('hScrollBarAlwaysVisible', false)
+          codeEditor.setOption('wrap', true)
+          codeEditor.setOption('showPrintMargin', false)
+
+          codeEditor.setValue(self.content)
+
+          codeEditor.focus()
+          codeEditor.renderer.updateFull()
+        }, 100)
+      },
+      loadMode (m) {
+        let self = this
+        if (self._.includes(self.modelistLoaded, m)) {
+          codeEditor.getSession().setMode('ace/mode/' + m)
+        } else {
+          self.$http.get('/js/ace/mode-' + m + '.js').then(resp => {
+            if(resp.ok) {
+              eval(resp.bodyText)
+              self.modelistLoaded.push(m)
+              ace.acequire('ace/mode/' + m)
+              codeEditor.getSession().setMode('ace/mode/' + m)
+            }
+          })
+        }
+      },
       cancel () {
-        this.$store.dispatch('editorCodeBlock/close')
+        codeEditor.destroy()
+        this.$store.dispatch('editorCodeblock/close')
       },
       insertCode () {
+        let codeBlockText = '\n```' + this.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'
+        this.$store.dispatch('editor/insert', codeBlockText)
         this.$store.dispatch('alert', {
-          style: 'pink',
+          style: 'blue',
           icon: 'inbox',
           msg: 'Your code block has been inserted.'
         })
         this.cancel()
       }
+    },
+    mounted() {
+      FuseBox.import('/js/ace/ace.js', (acePkg) => {
+        ace = acePkg
+        this.modes = ace.acequire('ace/ext/modelist').modesByName
+      })
+      this.$root.$on('editorCodeblock/init', this.init)
     }
   }
 </script>

+ 18 - 11
client/js/components/editor.component.js

@@ -1,7 +1,5 @@
 'use strict'
 
-/* global FuseBox */
-
 import filesize from 'filesize.js'
 import $ from 'jquery'
 
@@ -18,7 +16,18 @@ export default {
   data() {
     return {}
   },
+  computed: {
+    insertContent() {
+      return this.$store.state.editor.insertContent
+    }
+  },
   methods: {
+    insert(content) {
+      if (mde.codemirror.doc.somethingSelected()) {
+        mde.codemirror.execCommand('singleSelection')
+      }
+      mde.codemirror.doc.replaceSelection(this.insertContent)
+    },
     save() {
       let self = this
       this.$http.put(window.location.href, {
@@ -177,13 +186,9 @@ export default {
           {
             name: 'code-block',
             action: (editor) => {
-              // if (!mdeModalOpenState) {
-              //   if (mde.codemirror.doc.somethingSelected()) {
-              //     vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
-              //   }
-
-              //   vueCodeBlock.open()
-              // }
+              self.$store.dispatch('editorCodeblock/open', {
+                initialContent: (mde.codemirror.doc.somethingSelected()) ? mde.codemirror.doc.getSelection() : ''
+              })
             },
             className: 'icon-code',
             title: 'Code Block'
@@ -212,8 +217,6 @@ export default {
       })
 
       // Save
-
-      this.$root.$on('editor-save', this.save)
       $(window).bind('keydown', (ev) => {
         if (ev.ctrlKey || ev.metaKey) {
           switch (String.fromCharCode(ev.which).toLowerCase()) {
@@ -225,6 +228,10 @@ export default {
         }
       })
 
+      // Listeners
+      this.$root.$on('editor/save', this.save)
+      this.$root.$on('editor/insert', this.insert)
+
       this.$store.dispatch('pageLoader/complete')
     })
   }

+ 2 - 3
client/js/pages/source-view.component.js

@@ -1,7 +1,5 @@
 'use strict'
 
-/* global FuseBox */
-
 export default {
   name: 'source-view',
   data() {
@@ -9,13 +7,14 @@ export default {
   },
   mounted() {
     let self = this
-    FuseBox.import('/js/ace/source-view.js', (ace) => {
+    FuseBox.import('/js/ace/ace.js', (ace) => {
       let scEditor = ace.edit('source-display')
       scEditor.setTheme('ace/theme/dawn')
       scEditor.getSession().setMode('ace/mode/markdown')
       scEditor.setOption('fontSize', '14px')
       scEditor.setOption('hScrollBarAlwaysVisible', false)
       scEditor.setOption('wrap', true)
+      scEditor.setOption('showPrintMargin', false)
       scEditor.setReadOnly(true)
       scEditor.renderer.updateFull()
       scEditor.renderer.on('afterRender', () => {

+ 9 - 3
client/js/store/modules/editor-codeblock.js

@@ -3,14 +3,20 @@
 export default {
   namespaced: true,
   state: {
-    shown: false
+    shown: false,
+    content: ''
   },
   getters: {},
   mutations: {
-    shownChange: (state, shownState) => { state.shown = shownState }
+    shownChange: (state, shownState) => { state.shown = shownState },
+    contentChange: (state, newContent) => { state.content = newContent }
   },
   actions: {
-    open({ commit }) { commit('shownChange', true) },
+    open({ commit }, opts) {
+      commit('shownChange', true)
+      commit('contentChange', opts.initialContent || '')
+      wikijs.$emit('editorCodeblock/init')
+    },
     close({ commit }) { commit('shownChange', false) }
   }
 }

+ 16 - 3
client/js/store/modules/editor.js

@@ -2,8 +2,21 @@
 
 export default {
   namespaced: true,
-  state: {},
+  state: {
+    busy: false,
+    insertContent: ''
+  },
   getters: {},
-  mutations: {},
-  actions: {}
+  mutations: {
+    busyChange: (state, busyState) => { state.shown = busyState },
+    insertContentChange: (state, newContent) => { state.insertContent = newContent }
+  },
+  actions: {
+    busyStart({ commit }) { commit('busyChange', true) },
+    busyStop({ commit }) { commit('busyChange', false) },
+    insert({ commit }, content) {
+      commit('insertContentChange', content)
+      wikijs.$emit('editor/insert')
+    }
+  }
 }

+ 2 - 160
fuse.js

@@ -6,14 +6,9 @@
  * Client & Server compiler / bundler / watcher
  */
 
-const _ = require('lodash')
-const Promise = require('bluebird')
 const colors = require('colors/safe')
-const fs = Promise.promisifyAll(require('fs-extra'))
 const fsbx = require('fuse-box')
 const nodemon = require('nodemon')
-const path = require('path')
-const uglify = require('uglify-es')
 
 // ======================================================
 // Parse cmd arguments
@@ -77,157 +72,7 @@ const SHIMS = {
 // ======================================================
 
 console.info(colors.white('└── ') + colors.green('Running global tasks...'))
-
-let globalTasks = Promise.mapSeries([
-  /**
-   * SimpleMDE
-   */
-  () => {
-    return fs.accessAsync('./assets/js/simplemde').then(() => {
-      console.info(colors.white('  └── ') + colors.magenta('SimpleMDE directory already exists. Task aborted.'))
-      return true
-    }).catch(err => {
-      if (err.code === 'ENOENT') {
-        console.info(colors.white('  └── ') + colors.green('Copy + Minify SimpleMDE to assets...'))
-        return fs.copy('./node_modules/simplemde/dist/simplemde.min.js', './assets/js/simplemde/simplemde.min.js')
-      } else {
-        throw err
-      }
-    })
-  },
-  /**
-   * ACE Modes
-   */
-  () => {
-    return fs.accessAsync('./assets/js/ace').then(() => {
-      console.info(colors.white('  └── ') + colors.magenta('ACE modes directory already exists. Task aborted.'))
-      return true
-    }).catch(err => {
-      if (err.code === 'ENOENT') {
-        console.info(colors.white('  └── ') + colors.green('Copy + Minify ACE modes to assets...'))
-        return fs.ensureDirAsync('./assets/js/ace').then(() => {
-          return Promise.join(
-            // Core
-            Promise.all([
-              fs.readFileAsync('./node_modules/brace/index.js', 'utf8'),
-              fs.readFileAsync('./node_modules/brace/theme/dawn.js', 'utf8'),
-              fs.readFileAsync('./node_modules/brace/mode/markdown.js', 'utf8')
-            ]).then(items => {
-              console.info(colors.white('      source-view.js'))
-              let result = uglify.minify(items.join(';\n'), { output: { 'max_line_len': 1000000 } })
-              return fs.writeFileAsync('./assets/js/ace/source-view.js', result.code)
-            }),
-            // Modes
-            fs.readdirAsync('./node_modules/brace/mode').then(modeList => {
-              return Promise.map(modeList, mdFile => {
-                return fs.readFileAsync(path.join('./node_modules/brace/mode', mdFile), 'utf8').then(modeCode => {
-                  console.info(colors.white('      mode-' + mdFile))
-                  let result = uglify.minify(modeCode, { output: { 'max_line_len': 1000000 } })
-                  return fs.writeFileAsync(path.join('./assets/js/ace', 'mode-' + mdFile), result.code)
-                })
-              }, { concurrency: 3 })
-            })
-          )
-        })
-      } else {
-        throw err
-      }
-    })
-  },
-  /**
-   * 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
-   */
-  () => {
-    console.info(colors.white('  └── ') + colors.green('Copying i18n client files...'))
-    return fs.ensureDirAsync('./assets/js/i18n').then(() => {
-      return fs.readJsonAsync('./server/locales/en/browser.json').then(enContent => {
-        return fs.readdirAsync('./server/locales').then(langs => {
-          return Promise.map(langs, lang => {
-            console.info(colors.white('      ' + lang + '.json'))
-            let outputPath = path.join('./assets/js/i18n', lang + '.json')
-            return fs.readJsonAsync(path.join('./server/locales', lang + '.json'), 'utf8').then((content) => {
-              return fs.outputJsonAsync(outputPath, _.defaultsDeep(content, enContent))
-            }).catch(err => { // eslint-disable-line handle-callback-err
-              return fs.outputJsonAsync(outputPath, enContent)
-            })
-          })
-        })
-      })
-    })
-  },
-  /**
-   * 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
-   */
-  () => {
-    console.info(colors.white('  └── ') + colors.green('Clearing fuse-box cache...'))
-    return fs.emptyDirAsync('./.fusebox')
-  }
-], f => { return f() })
+let globalTasks = require('./.build/_tasks')
 
 // ======================================================
 // Fuse Tasks
@@ -243,7 +88,7 @@ globalTasks.then(() => {
       fsbx.EnvPlugin({ NODE_ENV: (dev) ? 'development' : 'production' }),
       fsbx.VuePlugin(),
       ['.scss', fsbx.SassPlugin({ outputStyle: (dev) ? 'nested' : 'compressed' }), fsbx.CSSPlugin()],
-      dev && fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
+      fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
       fsbx.JSONPlugin(),
       !dev && fsbx.UglifyESPlugin({
         compress: { unused: false },
@@ -261,14 +106,11 @@ globalTasks.then(() => {
     })
   }
 
-  // const bundleLibs = fuse.bundle('libs').instructions('~ index.js - brace')
-  // const bundleApp = fuse.bundle('app').instructions('!> [index.js]')
   const bundleApp = fuse.bundle('app').instructions('> index.js')
   const bundleSetup = fuse.bundle('configure').instructions('> configure.js')
 
   switch (mode) {
     case 'dev':
-      // bundleLibs.watch()
       bundleApp.watch()
       break
     case 'dev-configure':

+ 3 - 3
package.json

@@ -90,7 +90,7 @@
     "moment": "^2.18.1",
     "moment-timezone": "^0.5.13",
     "mongodb": "^2.2.27",
-    "mongoose": "^4.10.2",
+    "mongoose": "^4.10.3",
     "multer": "^1.3.0",
     "node-graceful": "^0.2.3",
     "ora": "^1.2.0",
@@ -156,8 +156,8 @@
     "snyk": "latest",
     "twemoji-awesome": "^1.0.6",
     "typescript": "^2.3.3",
-    "uglify-es": "^3.0.11",
-    "vee-validate": "^2.0.0-rc.4",
+    "uglify-es": "^3.0.12",
+    "vee-validate": "^2.0.0-rc.5",
     "vue": "^2.3.3",
     "vue-clipboards": "^1.0.0",
     "vue-lodash": "^1.0.3",

+ 4 - 1
server/index.js

@@ -87,7 +87,10 @@ app.use(mw.security)
 // ----------------------------------------
 
 app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')))
-app.use(express.static(path.join(ROOTPATH, 'assets')))
+app.use(express.static(path.join(ROOTPATH, 'assets'), {
+  index: false,
+  maxAge: '7d'
+}))
 
 // ----------------------------------------
 // Passport Authentication

+ 2 - 1
server/views/pages/edit.pug

@@ -9,7 +9,7 @@ block rootNavRight
     a.button.is-outlined(v-on:click='$store.dispatch("modalDiscardPage/open")')
       i.icon-cross
       span= t('nav.discard')
-    a.button(v-on:click='$root.$emit("editor-save")')
+    a.button(v-on:click='$root.$emit("editor/save")')
       i.icon-check
       span= t('nav.savechanges')
 
@@ -18,5 +18,6 @@ block content
     .editor-area
       textarea(ref='editorTextArea')= pageData.markdown
 
+  editor-codeblock
   modal-discard-page(mode='edit', current-path=pageData.meta.path)
   page-loader(text=t('loading.editor'))

+ 11 - 26
yarn.lock

@@ -4491,13 +4491,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.10.6, moment@^2.15, moment@^2.16.1, m
   version "2.18.1"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
 
-mongodb-core@2.1.10:
-  version "2.1.10"
-  resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.10.tgz#eb290681d196d3346a492161aa2ea0905e63151b"
-  dependencies:
-    bson "~1.0.4"
-    require_optional "~1.0.0"
-
 mongodb-core@2.1.11:
   version "2.1.11"
   resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.11.tgz#1c38776ceb174997a99c28860eed9028da9b3e1a"
@@ -4505,15 +4498,7 @@ mongodb-core@2.1.11:
     bson "~1.0.4"
     require_optional "~1.0.0"
 
-mongodb@2.2.26:
-  version "2.2.26"
-  resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-2.2.26.tgz#1bd50c557c277c98e1a05da38c9839c4922b034a"
-  dependencies:
-    es6-promise "3.2.1"
-    mongodb-core "2.1.10"
-    readable-stream "2.2.7"
-
-"mongodb@>= 1.2.0 <3.0.0", mongodb@^2.2.27:
+mongodb@2.2.27, "mongodb@>= 1.2.0 <3.0.0", mongodb@^2.2.27:
   version "2.2.27"
   resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-2.2.27.tgz#34122034db66d983bcf6ab5adb26a24a70fef6e6"
   dependencies:
@@ -4521,15 +4506,15 @@ mongodb@2.2.26:
     mongodb-core "2.1.11"
     readable-stream "2.2.7"
 
-mongoose@*, mongoose@^4.10.2:
-  version "4.10.2"
-  resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-4.10.2.tgz#c7473ebada5f985cdac8e4182d40776b1aaf5352"
+mongoose@*, mongoose@^4.10.3:
+  version "4.10.3"
+  resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-4.10.3.tgz#5fbcd6549e67191ef30cab259644ff75f77026f9"
   dependencies:
     async "2.1.4"
     bson "~1.0.4"
     hooks-fixed "2.0.0"
     kareem "1.4.1"
-    mongodb "2.2.26"
+    mongodb "2.2.27"
     mpath "0.2.1"
     mpromise "0.5.5"
     mquery "2.3.1"
@@ -6863,9 +6848,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192"
 
-uglify-es@^3.0.11:
-  version "3.0.11"
-  resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.0.11.tgz#acc51dfd2eb3681a3620d9c64881c29d9159ef85"
+uglify-es@^3.0.12:
+  version "3.0.12"
+  resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.0.12.tgz#7734f9bb64bbf36dbb1e059358343c58553e52c1"
   dependencies:
     commander "~2.9.0"
     source-map "~0.5.1"
@@ -7065,9 +7050,9 @@ vasync@^1.6.4:
   dependencies:
     verror "1.6.0"
 
-vee-validate@^2.0.0-rc.4:
-  version "2.0.0-rc.4"
-  resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.0.0-rc.4.tgz#49234ee481488fad909ab68a3f39edbb87f7a656"
+vee-validate@^2.0.0-rc.5:
+  version "2.0.0-rc.5"
+  resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-2.0.0-rc.5.tgz#1de41866ddab3f8bebae3f3db653ef2c1c782fa0"
 
 verror@1.3.6:
   version "1.3.6"