Ver Fonte

refactor: lazy-load simplemde + minimal lodash

NGPixel há 8 anos atrás
pai
commit
ebe288a5b2

+ 3 - 0
.gitignore

@@ -31,3 +31,6 @@ config.yml
 /repo
 /data
 /uploads
+
+# IDE exclude
+.idea

+ 2 - 1
.vscode/settings.json

@@ -2,5 +2,6 @@
   "eslint.enable": true,
   "puglint.enable": true,
   "standard.enable": false,
-  "editor.formatOnSave": true
+  "editor.formatOnSave": true,
+  "editor.tabSize": 2
 }

+ 58 - 1
client/js/app.js

@@ -4,7 +4,6 @@
 /* eslint-disable no-new */
 
 import $ from 'jquery'
-import _ from 'lodash'
 import Vue from 'vue'
 import VueResource from 'vue-resource'
 import VueClipboards from 'vue-clipboards'
@@ -17,6 +16,33 @@ import VueI18Next from '@panter/vue-i18next'
 import 'jquery-smooth-scroll'
 import 'jquery-sticky'
 
+// ====================================
+// Load minimal lodash
+// ====================================
+
+import concat from 'lodash/concat'
+import cloneDeep from 'lodash/cloneDeep'
+import debounce from 'lodash/debounce'
+import deburr from 'lodash/deburr'
+import delay from 'lodash/delay'
+import filter from 'lodash/filter'
+import find from 'lodash/find'
+import findKey from 'lodash/findKey'
+import forEach from 'lodash/forEach'
+import includes from 'lodash/includes'
+import isEmpty from 'lodash/isEmpty'
+import isNil from 'lodash/isNil'
+import join from 'lodash/join'
+import kebabCase from 'lodash/kebabCase'
+import last from 'lodash/last'
+import map from 'lodash/map'
+import pullAt from 'lodash/pullAt'
+import reject from 'lodash/reject'
+import slice from 'lodash/slice'
+import split from 'lodash/split'
+import trim from 'lodash/trim'
+import toUpper from 'lodash/toUpper'
+
 // ====================================
 // Load Helpers
 // ====================================
@@ -30,6 +56,7 @@ import helpers from './helpers'
 import alertComponent from './components/alert.vue'
 import anchorComponent from './components/anchor.vue'
 import colorPickerComponent from './components/color-picker.vue'
+import editorCodeblockComponent from './components/editor-codeblock.vue'
 import loadingSpinnerComponent from './components/loading-spinner.vue'
 import modalCreatePageComponent from './components/modal-create-page.vue'
 import modalCreateUserComponent from './components/modal-create-user.vue'
@@ -45,6 +72,35 @@ import contentViewComponent from './pages/content-view.component.js'
 import editorComponent from './components/editor.component.js'
 import sourceViewComponent from './pages/source-view.component.js'
 
+// ====================================
+// Build lodash object
+// ====================================
+
+const _ = {
+  deburr,
+  concat,
+  cloneDeep,
+  debounce,
+  delay,
+  filter,
+  find,
+  findKey,
+  forEach,
+  includes,
+  isEmpty,
+  isNil,
+  join,
+  kebabCase,
+  last,
+  map,
+  pullAt,
+  reject,
+  slice,
+  split,
+  toUpper,
+  trim
+}
+
 // ====================================
 // Initialize Vue Modules
 // ====================================
@@ -101,6 +157,7 @@ $(() => {
       colorPicker: colorPickerComponent,
       contentView: contentViewComponent,
       editor: editorComponent,
+      editorCodeblock: editorCodeblockComponent,
       loadingSpinner: loadingSpinnerComponent,
       modalCreatePage: modalCreatePageComponent,
       modalCreateUser: modalCreateUserComponent,

+ 51 - 0
client/js/components/editor-codeblock.vue

@@ -0,0 +1,51 @@
+<template lang="pug">
+  transition(:duration="400")
+    .modal(v-show='isShown', v-cloak)
+      transition(name='modal-background')
+        .modal-background(v-show='isShown')
+      .modal-container
+        transition(name='modal-content')
+          .modal-content.is-expanded(v-show='isShown')
+            header.is-green
+              span Insert Code Block
+            section.is-gapless
+              .columns.is-stretched
+                .column.is-one-quarter.modal-sidebar.is-green(style={'max-width':'350px'})
+                  .model-sidebar-header Language
+                  .model-sidebar-content
+                    p.control.is-fullwidth
+                      select(v-model='modeSelected')
+                        option(v-for='mode in modes', v-bind:value='mode.name') {{ mode.caption }}
+                .column.ace-container
+                  #codeblock-editor
+            footer
+              a.button.is-grey.is-outlined(v-on:click='cancel') Discard
+              a.button.is-green(v-on:click='insertCode') Insert Code Block
+</template>
+
+<script>
+  export default {
+    name: 'editor-codeblock',
+    data () {
+      return {}
+    },
+    computed: {
+      isShown () {
+        return this.$store.state.editorCodeblock.shown
+      }
+    },
+    methods: {
+      cancel () {
+        this.$store.dispatch('editorCodeBlock/close')
+      },
+      insertCode () {
+        this.$store.dispatch('alert', {
+          style: 'pink',
+          icon: 'inbox',
+          msg: 'Your code block has been inserted.'
+        })
+        this.cancel()
+      }
+    }
+  }
+</script>

+ 176 - 169
client/js/components/editor.component.js

@@ -1,6 +1,7 @@
 'use strict'
 
-import SimpleMDE from 'simplemde'
+/* global FuseBox */
+
 import filesize from 'filesize.js'
 import $ from 'jquery'
 
@@ -45,180 +46,186 @@ export default {
   },
   mounted() {
     let self = this
-    mde = new SimpleMDE({
-      autofocus: true,
-      autoDownloadFontAwesome: false,
-      element: this.$refs.editorTextArea,
-      placeholder: 'Enter Markdown formatted content here...',
-      spellChecker: false,
-      status: false,
-      toolbar: [
-        {
-          name: 'bold',
-          action: SimpleMDE.toggleBold,
-          className: 'icon-bold',
-          title: 'Bold'
-        },
-        {
-          name: 'italic',
-          action: SimpleMDE.toggleItalic,
-          className: 'icon-italic',
-          title: 'Italic'
-        },
-        {
-          name: 'strikethrough',
-          action: SimpleMDE.toggleStrikethrough,
-          className: 'icon-strikethrough',
-          title: 'Strikethrough'
-        },
-        '|',
-        {
-          name: 'heading-1',
-          action: SimpleMDE.toggleHeading1,
-          className: 'icon-header fa-header-x fa-header-1',
-          title: 'Big Heading'
-        },
-        {
-          name: 'heading-2',
-          action: SimpleMDE.toggleHeading2,
-          className: 'icon-header fa-header-x fa-header-2',
-          title: 'Medium Heading'
-        },
-        {
-          name: 'heading-3',
-          action: SimpleMDE.toggleHeading3,
-          className: 'icon-header fa-header-x fa-header-3',
-          title: 'Small Heading'
-        },
-        {
-          name: 'quote',
-          action: SimpleMDE.toggleBlockquote,
-          className: 'icon-quote-left',
-          title: 'Quote'
-        },
-        '|',
-        {
-          name: 'unordered-list',
-          action: SimpleMDE.toggleUnorderedList,
-          className: 'icon-th-list',
-          title: 'Bullet List'
-        },
-        {
-          name: 'ordered-list',
-          action: SimpleMDE.toggleOrderedList,
-          className: 'icon-list-ol',
-          title: 'Numbered List'
-        },
-        '|',
-        {
-          name: 'link',
-          action: (editor) => {
-            /* if(!mdeModalOpenState) {
-              mdeModalOpenState = true;
-              $('#modal-editor-link').slideToggle();
-            } */
-            window.alert('Coming soon!')
-          },
-          className: 'icon-link2',
-          title: 'Insert Link'
-        },
-        {
-          name: 'image',
-          action: (editor) => {
-            if (!mdeModalOpenState) {
-              vueImage.open()
-            }
-          },
-          className: 'icon-image',
-          title: 'Insert Image'
-        },
-        {
-          name: 'file',
-          action: (editor) => {
-            if (!mdeModalOpenState) {
-              vueFile.open()
-            }
-          },
-          className: 'icon-paper',
-          title: 'Insert File'
-        },
-        {
-          name: 'video',
-          action: (editor) => {
-            if (!mdeModalOpenState) {
-              vueVideo.open()
-            }
-          },
-          className: 'icon-video-camera2',
-          title: 'Insert Video Player'
-        },
-        '|',
-        {
-          name: 'inline-code',
-          action: (editor) => {
-            if (!editor.codemirror.doc.somethingSelected()) {
-              return alerts.pushError('Invalid selection', 'You must select at least 1 character first.')
-            }
-            let curSel = editor.codemirror.doc.getSelections()
-            curSel = _.map(curSel, (s) => {
-              return '`' + s + '`'
-            })
-            editor.codemirror.doc.replaceSelections(curSel)
-          },
-          className: 'icon-terminal',
-          title: 'Inline Code'
-        },
-        {
-          name: 'code-block',
-          action: (editor) => {
-            if (!mdeModalOpenState) {
-              if (mde.codemirror.doc.somethingSelected()) {
-                vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
+    FuseBox.import('/js/simplemde/simplemde.min.js', (SimpleMDE) => {
+      mde = new SimpleMDE({
+        autofocus: true,
+        autoDownloadFontAwesome: false,
+        element: this.$refs.editorTextArea,
+        placeholder: 'Enter Markdown formatted content here...',
+        spellChecker: false,
+        status: false,
+        toolbar: [
+          {
+            name: 'bold',
+            action: SimpleMDE.toggleBold,
+            className: 'icon-bold',
+            title: 'Bold'
+          },
+          {
+            name: 'italic',
+            action: SimpleMDE.toggleItalic,
+            className: 'icon-italic',
+            title: 'Italic'
+          },
+          {
+            name: 'strikethrough',
+            action: SimpleMDE.toggleStrikethrough,
+            className: 'icon-strikethrough',
+            title: 'Strikethrough'
+          },
+          '|',
+          {
+            name: 'heading-1',
+            action: SimpleMDE.toggleHeading1,
+            className: 'icon-header fa-header-x fa-header-1',
+            title: 'Big Heading'
+          },
+          {
+            name: 'heading-2',
+            action: SimpleMDE.toggleHeading2,
+            className: 'icon-header fa-header-x fa-header-2',
+            title: 'Medium Heading'
+          },
+          {
+            name: 'heading-3',
+            action: SimpleMDE.toggleHeading3,
+            className: 'icon-header fa-header-x fa-header-3',
+            title: 'Small Heading'
+          },
+          {
+            name: 'quote',
+            action: SimpleMDE.toggleBlockquote,
+            className: 'icon-quote-left',
+            title: 'Quote'
+          },
+          '|',
+          {
+            name: 'unordered-list',
+            action: SimpleMDE.toggleUnorderedList,
+            className: 'icon-th-list',
+            title: 'Bullet List'
+          },
+          {
+            name: 'ordered-list',
+            action: SimpleMDE.toggleOrderedList,
+            className: 'icon-list-ol',
+            title: 'Numbered List'
+          },
+          '|',
+          {
+            name: 'link',
+            action: (editor) => {
+              /* if(!mdeModalOpenState) {
+                mdeModalOpenState = true;
+                $('#modal-editor-link').slideToggle();
+              } */
+              window.alert('Coming soon!')
+            },
+            className: 'icon-link2',
+            title: 'Insert Link'
+          },
+          {
+            name: 'image',
+            action: (editor) => {
+              // if (!mdeModalOpenState) {
+              //   vueImage.open()
+              // }
+            },
+            className: 'icon-image',
+            title: 'Insert Image'
+          },
+          {
+            name: 'file',
+            action: (editor) => {
+              // if (!mdeModalOpenState) {
+              //   vueFile.open()
+              // }
+            },
+            className: 'icon-paper',
+            title: 'Insert File'
+          },
+          {
+            name: 'video',
+            action: (editor) => {
+              // if (!mdeModalOpenState) {
+              //   vueVideo.open()
+              // }
+            },
+            className: 'icon-video-camera2',
+            title: 'Insert Video Player'
+          },
+          '|',
+          {
+            name: 'inline-code',
+            action: (editor) => {
+              if (!editor.codemirror.doc.somethingSelected()) {
+                return self.$store.dispatch('alert', {
+                  style: 'orange',
+                  icon: 'marquee',
+                  msg: 'Invalid selection. Select at least 1 character.'
+                })
               }
+              let curSel = editor.codemirror.doc.getSelections()
+              curSel = self._.map(curSel, (s) => {
+                return '`' + s + '`'
+              })
+              editor.codemirror.doc.replaceSelections(curSel)
+            },
+            className: 'icon-terminal',
+            title: 'Inline Code'
+          },
+          {
+            name: 'code-block',
+            action: (editor) => {
+              // if (!mdeModalOpenState) {
+              //   if (mde.codemirror.doc.somethingSelected()) {
+              //     vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
+              //   }
 
-              vueCodeBlock.open()
-            }
-          },
-          className: 'icon-code',
-          title: 'Code Block'
-        },
-        '|',
-        {
-          name: 'table',
-          action: (editor) => {
-            window.alert('Coming soon!')
-            // todo
-          },
-          className: 'icon-table',
-          title: 'Insert Table'
-        },
-        {
-          name: 'horizontal-rule',
-          action: SimpleMDE.drawHorizontalRule,
-          className: 'icon-minus2',
-          title: 'Horizontal Rule'
+              //   vueCodeBlock.open()
+              // }
+            },
+            className: 'icon-code',
+            title: 'Code Block'
+          },
+          '|',
+          {
+            name: 'table',
+            action: (editor) => {
+              window.alert('Coming soon!')
+              // todo
+            },
+            className: 'icon-table',
+            title: 'Insert Table'
+          },
+          {
+            name: 'horizontal-rule',
+            action: SimpleMDE.drawHorizontalRule,
+            className: 'icon-minus2',
+            title: 'Horizontal Rule'
+          }
+        ],
+        shortcuts: {
+          'toggleBlockquote': null,
+          'toggleFullScreen': null
         }
-      ],
-      shortcuts: {
-        'toggleBlockquote': null,
-        'toggleFullScreen': null
-      }
-    })
+      })
 
-    // Save
+      // Save
 
-    this.$root.$on('editor-save', this.save)
-    $(window).bind('keydown', (ev) => {
-      if (ev.ctrlKey || ev.metaKey) {
-        switch (String.fromCharCode(ev.which).toLowerCase()) {
-          case 's':
-            ev.preventDefault()
-            self.save()
-            break
+      this.$root.$on('editor-save', this.save)
+      $(window).bind('keydown', (ev) => {
+        if (ev.ctrlKey || ev.metaKey) {
+          switch (String.fromCharCode(ev.which).toLowerCase()) {
+            case 's':
+              ev.preventDefault()
+              self.save()
+              break
+          }
         }
-      }
-    })
+      })
 
-    this.$store.dispatch('pageLoader/complete')
+      this.$store.dispatch('pageLoader/complete')
+    })
   }
 }

+ 12 - 5
client/js/helpers/pages.js

@@ -1,6 +1,13 @@
 'use strict'
 
-import _ from 'lodash'
+import deburr from 'lodash/deburr'
+import filter from 'lodash/filter'
+import isEmpty from 'lodash/isEmpty'
+import join from 'lodash/join'
+import kebabCase from 'lodash/kebabCase'
+import map from 'lodash/map'
+import split from 'lodash/split'
+import trim from 'lodash/trim'
 
 module.exports = {
   /**
@@ -9,11 +16,11 @@ module.exports = {
    * @returns {string} Safe path
    */
   makeSafePath: (rawPath) => {
-    let rawParts = _.split(_.trim(rawPath), '/')
-    rawParts = _.map(rawParts, (r) => {
-      return _.kebabCase(_.deburr(_.trim(r)))
+    let rawParts = split(trim(rawPath), '/')
+    rawParts = map(rawParts, (r) => {
+      return kebabCase(deburr(trim(r)))
     })
 
-    return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
+    return join(filter(rawParts, (r) => { return !isEmpty(r) }), '/')
   }
 }

+ 2 - 0
client/js/store/index.js

@@ -4,6 +4,7 @@ import Vuex from 'vuex'
 import alert from './modules/alert'
 import anchor from './modules/anchor'
 import editor from './modules/editor'
+import editorCodeblock from './modules/editor-codeblock'
 import modalCreatePage from './modules/modal-create-page'
 import modalCreateUser from './modules/modal-create-user'
 import modalDiscardPage from './modules/modal-discard-page'
@@ -28,6 +29,7 @@ export default new Vuex.Store({
     alert,
     anchor,
     editor,
+    editorCodeblock,
     modalCreatePage,
     modalCreateUser,
     modalDiscardPage,

+ 2 - 2
client/js/store/modules/alert.js

@@ -1,6 +1,6 @@
 'use strict'
 
-import _ from 'lodash'
+import debounce from 'lodash/debounce'
 
 export default {
   state: {
@@ -24,7 +24,7 @@ export default {
       commit('alertChange', opts)
       dispatch('alertDismiss')
     },
-    alertDismiss: _.debounce(({ commit }) => {
+    alertDismiss: debounce(({ commit }) => {
       let opts = { shown: false }
       commit('alertChange', opts)
     }, 3000)

+ 16 - 0
client/js/store/modules/editor-codeblock.js

@@ -0,0 +1,16 @@
+'use strict'
+
+export default {
+  namespaced: true,
+  state: {
+    shown: false
+  },
+  getters: {},
+  mutations: {
+    shownChange: (state, shownState) => { state.shown = shownState }
+  },
+  actions: {
+    open({ commit }) { commit('shownChange', true) },
+    close({ commit }) { commit('shownChange', false) }
+  }
+}

+ 16 - 0
fuse.js

@@ -79,6 +79,22 @@ 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
    */