Fuse-box client scripts integration + deps update
This commit is contained in:
parent
f6c519c5dc
commit
fe0c4ce0c0
47234
assets/js/bundle.min.js
vendored
47234
assets/js/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
46305
assets/js/configure.min.js
vendored
46305
assets/js/configure.min.js
vendored
File diff suppressed because one or more lines are too long
@ -8,8 +8,6 @@ switch (logic) {
|
|||||||
require('./js/login.js')
|
require('./js/login.js')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
require('./node_modules/highlight.js/styles/tomorrow.css')
|
|
||||||
require('./node_modules/simplemde/dist/simplemde.min.css')
|
|
||||||
require('./scss/app.scss')
|
require('./scss/app.scss')
|
||||||
require('./js/app.js')
|
require('./js/app.js')
|
||||||
break
|
break
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
/* global alertsData */
|
/* global alertsData */
|
||||||
|
|
||||||
import jQuery from 'jquery'
|
import $ from 'jquery'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import Sticky from 'sticky-js'
|
|
||||||
import io from 'socket.io-client'
|
import io from 'socket.io-client'
|
||||||
import Alerts from './components/alerts.js'
|
import Alerts from './components/alerts.js'
|
||||||
/* eslint-disable spaced-comment */
|
import 'jquery-smooth-scroll'
|
||||||
|
import Sticky from 'sticky-js'
|
||||||
|
|
||||||
jQuery(document).ready(function ($) {
|
$(() => {
|
||||||
// ====================================
|
// ====================================
|
||||||
// Scroll
|
// Scroll
|
||||||
// ====================================
|
// ====================================
|
||||||
@ -45,24 +45,17 @@ jQuery(document).ready(function ($) {
|
|||||||
// Establish WebSocket connection
|
// Establish WebSocket connection
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
var socket = io(window.location.origin) // eslint-disable-line no-unused-vars
|
var socket = io(window.location.origin)
|
||||||
|
|
||||||
//=include components/search.js
|
require('./components/search.js')(socket)
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Pages logic
|
// Pages logic
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
//=include pages/view.js
|
require('./pages/view.js')(alerts)
|
||||||
//=include pages/create.js
|
// require('./pages/create.js')
|
||||||
//=include pages/edit.js
|
require('./pages/edit.js')(alerts, socket)
|
||||||
//=include pages/source.js
|
require('./pages/source.js')(alerts)
|
||||||
//=include pages/admin.js
|
require('./pages/admin.js')(alerts)
|
||||||
})
|
})
|
||||||
|
|
||||||
//=include helpers/form.js
|
|
||||||
//=include helpers/pages.js
|
|
||||||
|
|
||||||
//=include components/alerts.js
|
|
||||||
|
|
||||||
/* eslint-enable spaced-comment */
|
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
/* global $, Vue, ace, mde, _ */
|
'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'
|
||||||
|
|
||||||
let modelist = ace.require('ace/ext/modelist')
|
|
||||||
let codeEditor = null
|
let codeEditor = null
|
||||||
|
|
||||||
// ACE - Mode Loader
|
// ACE - Mode Loader
|
||||||
@ -24,52 +30,56 @@ let loadAceMode = (m) => {
|
|||||||
|
|
||||||
// Vue Code Block instance
|
// Vue Code Block instance
|
||||||
|
|
||||||
let vueCodeBlock = new Vue({
|
module.exports = (mde, mdeModalOpenState) => {
|
||||||
el: '#modal-editor-codeblock',
|
let modelist = {} // ace.require('ace/ext/modelist')
|
||||||
data: {
|
let vueCodeBlock = new Vue({
|
||||||
modes: modelist.modesByName,
|
el: '#modal-editor-codeblock',
|
||||||
modeSelected: 'text',
|
data: {
|
||||||
initContent: ''
|
modes: modelist.modesByName,
|
||||||
},
|
modeSelected: 'text',
|
||||||
watch: {
|
initContent: ''
|
||||||
modeSelected: (val, oldVal) => {
|
|
||||||
loadAceMode(val).done(() => {
|
|
||||||
ace.require('ace/mode/' + val)
|
|
||||||
codeEditor.getSession().setMode('ace/mode/' + val)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
open: (ev) => {
|
|
||||||
$('#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) => {
|
watch: {
|
||||||
mdeModalOpenState = false // eslint-disable-line no-undef
|
modeSelected: (val, oldVal) => {
|
||||||
$('#modal-editor-codeblock').removeClass('is-active')
|
loadAceMode(val).done(() => {
|
||||||
vueCodeBlock.initContent = ''
|
ace.require('ace/mode/' + val)
|
||||||
},
|
codeEditor.getSession().setMode('ace/mode/' + val)
|
||||||
insertCode: (ev) => {
|
})
|
||||||
if (mde.codemirror.doc.somethingSelected()) {
|
|
||||||
mde.codemirror.execCommand('singleSelection')
|
|
||||||
}
|
}
|
||||||
let codeBlockText = '\n```' + vueCodeBlock.modeSelected + '\n' + codeEditor.getValue() + '\n```\n'
|
},
|
||||||
|
methods: {
|
||||||
|
open: (ev) => {
|
||||||
|
$('#modal-editor-codeblock').addClass('is-active')
|
||||||
|
|
||||||
mde.codemirror.doc.replaceSelection(codeBlockText)
|
_.delay(() => {
|
||||||
vueCodeBlock.cancel()
|
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
|
||||||
|
}
|
||||||
|
@ -1,352 +1,361 @@
|
|||||||
/* global $, Vue, _, alerts, mde, socket */
|
'use strict'
|
||||||
|
|
||||||
let vueFile = new Vue({
|
import $ from 'jquery'
|
||||||
el: '#modal-editor-file',
|
import Vue from 'vue'
|
||||||
data: {
|
import _ from 'lodash'
|
||||||
isLoading: false,
|
import 'jquery-contextmenu'
|
||||||
isLoadingText: '',
|
import 'jquery-simple-upload'
|
||||||
newFolderName: '',
|
|
||||||
newFolderShow: false,
|
|
||||||
newFolderError: false,
|
|
||||||
folders: [],
|
|
||||||
currentFolder: '',
|
|
||||||
currentFile: '',
|
|
||||||
files: [],
|
|
||||||
uploadSucceeded: false,
|
|
||||||
postUploadChecks: 0,
|
|
||||||
renameFileShow: false,
|
|
||||||
renameFileId: '',
|
|
||||||
renameFileFilename: '',
|
|
||||||
deleteFileShow: false,
|
|
||||||
deleteFileId: '',
|
|
||||||
deleteFileFilename: ''
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
open: () => {
|
module.exports = (alerts, mde, mdeModalOpenState, socket) => {
|
||||||
mdeModalOpenState = true // eslint-disable-line no-undef
|
let vueFile = new Vue({
|
||||||
$('#modal-editor-file').addClass('is-active')
|
el: '#modal-editor-file',
|
||||||
vueFile.refreshFolders()
|
|
||||||
},
|
|
||||||
cancel: (ev) => {
|
|
||||||
mdeModalOpenState = false // eslint-disable-line no-undef
|
|
||||||
$('#modal-editor-file').removeClass('is-active')
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// INSERT LINK TO FILE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
selectFile: (fileId) => {
|
|
||||||
vueFile.currentFile = fileId
|
|
||||||
},
|
|
||||||
insertFileLink: (ev) => {
|
|
||||||
if (mde.codemirror.doc.somethingSelected()) {
|
|
||||||
mde.codemirror.execCommand('singleSelection')
|
|
||||||
}
|
|
||||||
|
|
||||||
let selFile = _.find(vueFile.files, ['_id', vueFile.currentFile])
|
|
||||||
selFile.normalizedPath = (selFile.folder === 'f:') ? selFile.filename : selFile.folder.slice(2) + '/' + selFile.filename
|
|
||||||
selFile.titleGuess = _.startCase(selFile.basename)
|
|
||||||
|
|
||||||
let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")'
|
|
||||||
|
|
||||||
mde.codemirror.doc.replaceSelection(fileText)
|
|
||||||
vueFile.cancel()
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// NEW FOLDER
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
newFolder: (ev) => {
|
|
||||||
vueFile.newFolderName = ''
|
|
||||||
vueFile.newFolderError = false
|
|
||||||
vueFile.newFolderShow = true
|
|
||||||
_.delay(() => { $('#txt-editor-file-newfoldername').focus() }, 400)
|
|
||||||
},
|
|
||||||
newFolderDiscard: (ev) => {
|
|
||||||
vueFile.newFolderShow = false
|
|
||||||
},
|
|
||||||
newFolderCreate: (ev) => {
|
|
||||||
let regFolderName = new RegExp('^[a-z0-9][a-z0-9-]*[a-z0-9]$')
|
|
||||||
vueFile.newFolderName = _.kebabCase(_.trim(vueFile.newFolderName))
|
|
||||||
|
|
||||||
if (_.isEmpty(vueFile.newFolderName) || !regFolderName.test(vueFile.newFolderName)) {
|
|
||||||
vueFile.newFolderError = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vueFile.newFolderDiscard()
|
|
||||||
vueFile.isLoadingText = 'Creating new folder...'
|
|
||||||
vueFile.isLoading = true
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsCreateFolder', { foldername: vueFile.newFolderName }, (data) => {
|
|
||||||
vueFile.folders = data
|
|
||||||
vueFile.currentFolder = vueFile.newFolderName
|
|
||||||
vueFile.files = []
|
|
||||||
vueFile.isLoading = false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// RENAME FILE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
renameFile: () => {
|
|
||||||
let c = _.find(vueFile.files, [ '_id', vueFile.renameFileId ])
|
|
||||||
vueFile.renameFileFilename = c.basename || ''
|
|
||||||
vueFile.renameFileShow = true
|
|
||||||
_.delay(() => {
|
|
||||||
$('#txt-editor-renamefile').focus()
|
|
||||||
_.defer(() => { $('#txt-editor-file-rename').select() })
|
|
||||||
}, 400)
|
|
||||||
},
|
|
||||||
renameFileDiscard: () => {
|
|
||||||
vueFile.renameFileShow = false
|
|
||||||
},
|
|
||||||
renameFileGo: () => {
|
|
||||||
vueFile.renameFileDiscard()
|
|
||||||
vueFile.isLoadingText = 'Renaming file...'
|
|
||||||
vueFile.isLoading = true
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsRenameFile', { uid: vueFile.renameFileId, folder: vueFile.currentFolder, filename: vueFile.renameFileFilename }, (data) => {
|
|
||||||
if (data.ok) {
|
|
||||||
vueFile.waitChangeComplete(vueFile.files.length, false)
|
|
||||||
} else {
|
|
||||||
vueFile.isLoading = false
|
|
||||||
alerts.pushError('Rename error', data.msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// MOVE FILE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
moveFile: (uid, fld) => {
|
|
||||||
vueFile.isLoadingText = 'Moving file...'
|
|
||||||
vueFile.isLoading = true
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
|
|
||||||
if (data.ok) {
|
|
||||||
vueFile.loadFiles()
|
|
||||||
} else {
|
|
||||||
vueFile.isLoading = false
|
|
||||||
alerts.pushError('Rename error', data.msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// DELETE FILE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
deleteFileWarn: (show) => {
|
|
||||||
if (show) {
|
|
||||||
let c = _.find(vueFile.files, [ '_id', vueFile.deleteFileId ])
|
|
||||||
vueFile.deleteFileFilename = c.filename || 'this file'
|
|
||||||
}
|
|
||||||
vueFile.deleteFileShow = show
|
|
||||||
},
|
|
||||||
deleteFileGo: () => {
|
|
||||||
vueFile.deleteFileWarn(false)
|
|
||||||
vueFile.isLoadingText = 'Deleting file...'
|
|
||||||
vueFile.isLoading = true
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsDeleteFile', { uid: vueFile.deleteFileId }, (data) => {
|
|
||||||
vueFile.loadFiles()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// LOAD FROM REMOTE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
selectFolder: (fldName) => {
|
|
||||||
vueFile.currentFolder = fldName
|
|
||||||
vueFile.loadFiles()
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshFolders: () => {
|
|
||||||
vueFile.isLoadingText = 'Fetching folders list...'
|
|
||||||
vueFile.isLoading = true
|
|
||||||
vueFile.currentFolder = ''
|
|
||||||
vueFile.currentImage = ''
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsGetFolders', { }, (data) => {
|
|
||||||
vueFile.folders = data
|
|
||||||
vueFile.loadFiles()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
loadFiles: (silent) => {
|
|
||||||
if (!silent) {
|
|
||||||
vueFile.isLoadingText = 'Fetching files...'
|
|
||||||
vueFile.isLoading = true
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsGetFiles', { folder: vueFile.currentFolder }, (data) => {
|
|
||||||
vueFile.files = data
|
|
||||||
if (!silent) {
|
|
||||||
vueFile.isLoading = false
|
|
||||||
}
|
|
||||||
vueFile.attachContextMenus()
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
waitChangeComplete: (oldAmount, expectChange) => {
|
|
||||||
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
|
|
||||||
|
|
||||||
vueFile.postUploadChecks++
|
|
||||||
vueFile.isLoadingText = 'Processing...'
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
vueFile.loadFiles(true).then(() => {
|
|
||||||
if ((vueFile.files.length !== oldAmount) === expectChange) {
|
|
||||||
vueFile.postUploadChecks = 0
|
|
||||||
vueFile.isLoading = false
|
|
||||||
} else if (vueFile.postUploadChecks > 5) {
|
|
||||||
vueFile.postUploadChecks = 0
|
|
||||||
vueFile.isLoading = false
|
|
||||||
alerts.pushError('Unable to fetch updated listing', 'Try again later')
|
|
||||||
} else {
|
|
||||||
_.delay(() => {
|
|
||||||
vueFile.waitChangeComplete(oldAmount, expectChange)
|
|
||||||
}, 1500)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// IMAGE CONTEXT MENU
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
attachContextMenus: () => {
|
|
||||||
let moveFolders = _.map(vueFile.folders, (f) => {
|
|
||||||
return {
|
|
||||||
name: (f !== '') ? f : '/ (root)',
|
|
||||||
icon: 'fa-folder',
|
|
||||||
callback: (key, opt) => {
|
|
||||||
let moveFileId = _.toString($(opt.$trigger).data('uid'))
|
|
||||||
let moveFileDestFolder = _.nth(vueFile.folders, key)
|
|
||||||
vueFile.moveFile(moveFileId, moveFileDestFolder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$.contextMenu('destroy', '.editor-modal-file-choices > figure')
|
|
||||||
$.contextMenu({
|
|
||||||
selector: '.editor-modal-file-choices > figure',
|
|
||||||
appendTo: '.editor-modal-file-choices',
|
|
||||||
position: (opt, x, y) => {
|
|
||||||
$(opt.$trigger).addClass('is-contextopen')
|
|
||||||
let trigPos = $(opt.$trigger).position()
|
|
||||||
let trigDim = { w: $(opt.$trigger).width() / 5, h: $(opt.$trigger).height() / 2 }
|
|
||||||
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
hide: (opt) => {
|
|
||||||
$(opt.$trigger).removeClass('is-contextopen')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items: {
|
|
||||||
rename: {
|
|
||||||
name: 'Rename',
|
|
||||||
icon: 'fa-edit',
|
|
||||||
callback: (key, opt) => {
|
|
||||||
vueFile.renameFileId = _.toString(opt.$trigger[0].dataset.uid)
|
|
||||||
vueFile.renameFile()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move: {
|
|
||||||
name: 'Move to...',
|
|
||||||
icon: 'fa-folder-open-o',
|
|
||||||
items: moveFolders
|
|
||||||
},
|
|
||||||
delete: {
|
|
||||||
name: 'Delete',
|
|
||||||
icon: 'fa-trash',
|
|
||||||
callback: (key, opt) => {
|
|
||||||
vueFile.deleteFileId = _.toString(opt.$trigger[0].dataset.uid)
|
|
||||||
vueFile.deleteFileWarn(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$('#btn-editor-file-upload input').on('change', (ev) => {
|
|
||||||
let curFileAmount = vueFile.files.length
|
|
||||||
|
|
||||||
$(ev.currentTarget).simpleUpload('/uploads/file', {
|
|
||||||
|
|
||||||
name: 'binfile',
|
|
||||||
data: {
|
data: {
|
||||||
folder: vueFile.currentFolder
|
isLoading: false,
|
||||||
|
isLoadingText: '',
|
||||||
|
newFolderName: '',
|
||||||
|
newFolderShow: false,
|
||||||
|
newFolderError: false,
|
||||||
|
folders: [],
|
||||||
|
currentFolder: '',
|
||||||
|
currentFile: '',
|
||||||
|
files: [],
|
||||||
|
uploadSucceeded: false,
|
||||||
|
postUploadChecks: 0,
|
||||||
|
renameFileShow: false,
|
||||||
|
renameFileId: '',
|
||||||
|
renameFileFilename: '',
|
||||||
|
deleteFileShow: false,
|
||||||
|
deleteFileId: '',
|
||||||
|
deleteFileFilename: ''
|
||||||
},
|
},
|
||||||
limit: 20,
|
methods: {
|
||||||
expect: 'json',
|
|
||||||
maxFileSize: 0,
|
|
||||||
|
|
||||||
init: (totalUploads) => {
|
open: () => {
|
||||||
vueFile.uploadSucceeded = false
|
mdeModalOpenState = true // eslint-disable-line no-undef
|
||||||
vueFile.isLoadingText = 'Preparing to upload...'
|
$('#modal-editor-file').addClass('is-active')
|
||||||
vueFile.isLoading = true
|
vueFile.refreshFolders()
|
||||||
},
|
},
|
||||||
|
cancel: (ev) => {
|
||||||
|
mdeModalOpenState = false // eslint-disable-line no-undef
|
||||||
|
$('#modal-editor-file').removeClass('is-active')
|
||||||
|
},
|
||||||
|
|
||||||
progress: (progress) => {
|
// -------------------------------------------
|
||||||
vueFile.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
|
// INSERT LINK TO FILE
|
||||||
},
|
// -------------------------------------------
|
||||||
|
|
||||||
success: (data) => {
|
selectFile: (fileId) => {
|
||||||
if (data.ok) {
|
vueFile.currentFile = fileId
|
||||||
let failedUpls = _.filter(data.results, ['ok', false])
|
},
|
||||||
if (failedUpls.length) {
|
insertFileLink: (ev) => {
|
||||||
_.forEach(failedUpls, (u) => {
|
if (mde.codemirror.doc.somethingSelected()) {
|
||||||
alerts.pushError('Upload error', u.msg)
|
mde.codemirror.execCommand('singleSelection')
|
||||||
|
}
|
||||||
|
|
||||||
|
let selFile = _.find(vueFile.files, ['_id', vueFile.currentFile])
|
||||||
|
selFile.normalizedPath = (selFile.folder === 'f:') ? selFile.filename : selFile.folder.slice(2) + '/' + selFile.filename
|
||||||
|
selFile.titleGuess = _.startCase(selFile.basename)
|
||||||
|
|
||||||
|
let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")'
|
||||||
|
|
||||||
|
mde.codemirror.doc.replaceSelection(fileText)
|
||||||
|
vueFile.cancel()
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// NEW FOLDER
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
newFolder: (ev) => {
|
||||||
|
vueFile.newFolderName = ''
|
||||||
|
vueFile.newFolderError = false
|
||||||
|
vueFile.newFolderShow = true
|
||||||
|
_.delay(() => { $('#txt-editor-file-newfoldername').focus() }, 400)
|
||||||
|
},
|
||||||
|
newFolderDiscard: (ev) => {
|
||||||
|
vueFile.newFolderShow = false
|
||||||
|
},
|
||||||
|
newFolderCreate: (ev) => {
|
||||||
|
let regFolderName = new RegExp('^[a-z0-9][a-z0-9-]*[a-z0-9]$')
|
||||||
|
vueFile.newFolderName = _.kebabCase(_.trim(vueFile.newFolderName))
|
||||||
|
|
||||||
|
if (_.isEmpty(vueFile.newFolderName) || !regFolderName.test(vueFile.newFolderName)) {
|
||||||
|
vueFile.newFolderError = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vueFile.newFolderDiscard()
|
||||||
|
vueFile.isLoadingText = 'Creating new folder...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsCreateFolder', { foldername: vueFile.newFolderName }, (data) => {
|
||||||
|
vueFile.folders = data
|
||||||
|
vueFile.currentFolder = vueFile.newFolderName
|
||||||
|
vueFile.files = []
|
||||||
|
vueFile.isLoading = false
|
||||||
})
|
})
|
||||||
if (failedUpls.length < data.results.length) {
|
})
|
||||||
alerts.push({
|
},
|
||||||
title: 'Some uploads succeeded',
|
|
||||||
message: 'Files that are not mentionned in the errors above were uploaded successfully.'
|
// -------------------------------------------
|
||||||
|
// RENAME FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
renameFile: () => {
|
||||||
|
let c = _.find(vueFile.files, [ '_id', vueFile.renameFileId ])
|
||||||
|
vueFile.renameFileFilename = c.basename || ''
|
||||||
|
vueFile.renameFileShow = true
|
||||||
|
_.delay(() => {
|
||||||
|
$('#txt-editor-renamefile').focus()
|
||||||
|
_.defer(() => { $('#txt-editor-file-rename').select() })
|
||||||
|
}, 400)
|
||||||
|
},
|
||||||
|
renameFileDiscard: () => {
|
||||||
|
vueFile.renameFileShow = false
|
||||||
|
},
|
||||||
|
renameFileGo: () => {
|
||||||
|
vueFile.renameFileDiscard()
|
||||||
|
vueFile.isLoadingText = 'Renaming file...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsRenameFile', { uid: vueFile.renameFileId, folder: vueFile.currentFolder, filename: vueFile.renameFileFilename }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
vueFile.waitChangeComplete(vueFile.files.length, false)
|
||||||
|
} else {
|
||||||
|
vueFile.isLoading = false
|
||||||
|
alerts.pushError('Rename error', data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// MOVE FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
moveFile: (uid, fld) => {
|
||||||
|
vueFile.isLoadingText = 'Moving file...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
vueFile.loadFiles()
|
||||||
|
} else {
|
||||||
|
vueFile.isLoading = false
|
||||||
|
alerts.pushError('Rename error', data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// DELETE FILE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
deleteFileWarn: (show) => {
|
||||||
|
if (show) {
|
||||||
|
let c = _.find(vueFile.files, [ '_id', vueFile.deleteFileId ])
|
||||||
|
vueFile.deleteFileFilename = c.filename || 'this file'
|
||||||
|
}
|
||||||
|
vueFile.deleteFileShow = show
|
||||||
|
},
|
||||||
|
deleteFileGo: () => {
|
||||||
|
vueFile.deleteFileWarn(false)
|
||||||
|
vueFile.isLoadingText = 'Deleting file...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsDeleteFile', { uid: vueFile.deleteFileId }, (data) => {
|
||||||
|
vueFile.loadFiles()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// LOAD FROM REMOTE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
selectFolder: (fldName) => {
|
||||||
|
vueFile.currentFolder = fldName
|
||||||
|
vueFile.loadFiles()
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshFolders: () => {
|
||||||
|
vueFile.isLoadingText = 'Fetching folders list...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
vueFile.currentFolder = ''
|
||||||
|
vueFile.currentImage = ''
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsGetFolders', { }, (data) => {
|
||||||
|
vueFile.folders = data
|
||||||
|
vueFile.loadFiles()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
loadFiles: (silent) => {
|
||||||
|
if (!silent) {
|
||||||
|
vueFile.isLoadingText = 'Fetching files...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsGetFiles', { folder: vueFile.currentFolder }, (data) => {
|
||||||
|
vueFile.files = data
|
||||||
|
if (!silent) {
|
||||||
|
vueFile.isLoading = false
|
||||||
|
}
|
||||||
|
vueFile.attachContextMenus()
|
||||||
|
resolve(true)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
waitChangeComplete: (oldAmount, expectChange) => {
|
||||||
|
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
|
||||||
|
|
||||||
|
vueFile.postUploadChecks++
|
||||||
|
vueFile.isLoadingText = 'Processing...'
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
vueFile.loadFiles(true).then(() => {
|
||||||
|
if ((vueFile.files.length !== oldAmount) === expectChange) {
|
||||||
|
vueFile.postUploadChecks = 0
|
||||||
|
vueFile.isLoading = false
|
||||||
|
} else if (vueFile.postUploadChecks > 5) {
|
||||||
|
vueFile.postUploadChecks = 0
|
||||||
|
vueFile.isLoading = false
|
||||||
|
alerts.pushError('Unable to fetch updated listing', 'Try again later')
|
||||||
|
} else {
|
||||||
|
_.delay(() => {
|
||||||
|
vueFile.waitChangeComplete(oldAmount, expectChange)
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// IMAGE CONTEXT MENU
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
attachContextMenus: () => {
|
||||||
|
let moveFolders = _.map(vueFile.folders, (f) => {
|
||||||
|
return {
|
||||||
|
name: (f !== '') ? f : '/ (root)',
|
||||||
|
icon: 'fa-folder',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
let moveFileId = _.toString($(opt.$trigger).data('uid'))
|
||||||
|
let moveFileDestFolder = _.nth(vueFile.folders, key)
|
||||||
|
vueFile.moveFile(moveFileId, moveFileDestFolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$.contextMenu('destroy', '.editor-modal-file-choices > figure')
|
||||||
|
$.contextMenu({
|
||||||
|
selector: '.editor-modal-file-choices > figure',
|
||||||
|
appendTo: '.editor-modal-file-choices',
|
||||||
|
position: (opt, x, y) => {
|
||||||
|
$(opt.$trigger).addClass('is-contextopen')
|
||||||
|
let trigPos = $(opt.$trigger).position()
|
||||||
|
let trigDim = { w: $(opt.$trigger).width() / 5, h: $(opt.$trigger).height() / 2 }
|
||||||
|
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
hide: (opt) => {
|
||||||
|
$(opt.$trigger).removeClass('is-contextopen')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
rename: {
|
||||||
|
name: 'Rename',
|
||||||
|
icon: 'fa-edit',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
vueFile.renameFileId = _.toString(opt.$trigger[0].dataset.uid)
|
||||||
|
vueFile.renameFile()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move: {
|
||||||
|
name: 'Move to...',
|
||||||
|
icon: 'fa-folder-open-o',
|
||||||
|
items: moveFolders
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
name: 'Delete',
|
||||||
|
icon: 'fa-trash',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
vueFile.deleteFileId = _.toString(opt.$trigger[0].dataset.uid)
|
||||||
|
vueFile.deleteFileWarn(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#btn-editor-file-upload input').on('change', (ev) => {
|
||||||
|
let curFileAmount = vueFile.files.length
|
||||||
|
|
||||||
|
$(ev.currentTarget).simpleUpload('/uploads/file', {
|
||||||
|
|
||||||
|
name: 'binfile',
|
||||||
|
data: {
|
||||||
|
folder: vueFile.currentFolder
|
||||||
|
},
|
||||||
|
limit: 20,
|
||||||
|
expect: 'json',
|
||||||
|
maxFileSize: 0,
|
||||||
|
|
||||||
|
init: (totalUploads) => {
|
||||||
|
vueFile.uploadSucceeded = false
|
||||||
|
vueFile.isLoadingText = 'Preparing to upload...'
|
||||||
|
vueFile.isLoading = true
|
||||||
|
},
|
||||||
|
|
||||||
|
progress: (progress) => {
|
||||||
|
vueFile.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
|
||||||
|
},
|
||||||
|
|
||||||
|
success: (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
let failedUpls = _.filter(data.results, ['ok', false])
|
||||||
|
if (failedUpls.length) {
|
||||||
|
_.forEach(failedUpls, (u) => {
|
||||||
|
alerts.pushError('Upload error', u.msg)
|
||||||
|
})
|
||||||
|
if (failedUpls.length < data.results.length) {
|
||||||
|
alerts.push({
|
||||||
|
title: 'Some uploads succeeded',
|
||||||
|
message: 'Files that are not mentionned in the errors above were uploaded successfully.'
|
||||||
|
})
|
||||||
|
vueFile.uploadSucceeded = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
vueFile.uploadSucceeded = true
|
vueFile.uploadSucceeded = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vueFile.uploadSucceeded = true
|
alerts.pushError('Upload error', data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
error: (error) => {
|
||||||
|
alerts.pushError('Upload error', error.message)
|
||||||
|
},
|
||||||
|
|
||||||
|
finish: () => {
|
||||||
|
if (vueFile.uploadSucceeded) {
|
||||||
|
vueFile.waitChangeComplete(curFileAmount, true)
|
||||||
|
} else {
|
||||||
|
vueFile.isLoading = false
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
alerts.pushError('Upload error', data.msg)
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
error: (error) => {
|
|
||||||
alerts.pushError('Upload error', error.message)
|
|
||||||
},
|
|
||||||
|
|
||||||
finish: () => {
|
|
||||||
if (vueFile.uploadSucceeded) {
|
|
||||||
vueFile.waitChangeComplete(curFileAmount, true)
|
|
||||||
} else {
|
|
||||||
vueFile.isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
return vueFile
|
||||||
|
}
|
||||||
|
@ -1,397 +1,406 @@
|
|||||||
/* global $, Vue, mde, _, alerts, socket */
|
'use strict'
|
||||||
|
|
||||||
let vueImage = new Vue({
|
import $ from 'jquery'
|
||||||
el: '#modal-editor-image',
|
import Vue from 'vue'
|
||||||
data: {
|
import _ from 'lodash'
|
||||||
isLoading: false,
|
import 'jquery-contextmenu'
|
||||||
isLoadingText: '',
|
import 'jquery-simple-upload'
|
||||||
newFolderName: '',
|
|
||||||
newFolderShow: false,
|
|
||||||
newFolderError: false,
|
|
||||||
fetchFromUrlURL: '',
|
|
||||||
fetchFromUrlShow: false,
|
|
||||||
folders: [],
|
|
||||||
currentFolder: '',
|
|
||||||
currentImage: '',
|
|
||||||
currentAlign: 'left',
|
|
||||||
images: [],
|
|
||||||
uploadSucceeded: false,
|
|
||||||
postUploadChecks: 0,
|
|
||||||
renameImageShow: false,
|
|
||||||
renameImageId: '',
|
|
||||||
renameImageFilename: '',
|
|
||||||
deleteImageShow: false,
|
|
||||||
deleteImageId: '',
|
|
||||||
deleteImageFilename: ''
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
open: () => {
|
module.exports = (alerts, mde, mdeModalOpenState, socket) => {
|
||||||
mdeModalOpenState = true // eslint-disable-line no-undef
|
let vueImage = new Vue({
|
||||||
$('#modal-editor-image').addClass('is-active')
|
el: '#modal-editor-image',
|
||||||
vueImage.refreshFolders()
|
|
||||||
},
|
|
||||||
cancel: (ev) => {
|
|
||||||
mdeModalOpenState = false // eslint-disable-line no-undef
|
|
||||||
$('#modal-editor-image').removeClass('is-active')
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// INSERT IMAGE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
selectImage: (imageId) => {
|
|
||||||
vueImage.currentImage = imageId
|
|
||||||
},
|
|
||||||
insertImage: (ev) => {
|
|
||||||
if (mde.codemirror.doc.somethingSelected()) {
|
|
||||||
mde.codemirror.execCommand('singleSelection')
|
|
||||||
}
|
|
||||||
|
|
||||||
let selImage = _.find(vueImage.images, ['_id', vueImage.currentImage])
|
|
||||||
selImage.normalizedPath = (selImage.folder === 'f:') ? selImage.filename : selImage.folder.slice(2) + '/' + selImage.filename
|
|
||||||
selImage.titleGuess = _.startCase(selImage.basename)
|
|
||||||
|
|
||||||
let imageText = '![' + selImage.titleGuess + '](/uploads/' + selImage.normalizedPath + ' "' + selImage.titleGuess + '")'
|
|
||||||
switch (vueImage.currentAlign) {
|
|
||||||
case 'center':
|
|
||||||
imageText += '{.align-center}'
|
|
||||||
break
|
|
||||||
case 'right':
|
|
||||||
imageText += '{.align-right}'
|
|
||||||
break
|
|
||||||
case 'logo':
|
|
||||||
imageText += '{.pagelogo}'
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
mde.codemirror.doc.replaceSelection(imageText)
|
|
||||||
vueImage.cancel()
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// NEW FOLDER
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
newFolder: (ev) => {
|
|
||||||
vueImage.newFolderName = ''
|
|
||||||
vueImage.newFolderError = false
|
|
||||||
vueImage.newFolderShow = true
|
|
||||||
_.delay(() => { $('#txt-editor-image-newfoldername').focus() }, 400)
|
|
||||||
},
|
|
||||||
newFolderDiscard: (ev) => {
|
|
||||||
vueImage.newFolderShow = false
|
|
||||||
},
|
|
||||||
newFolderCreate: (ev) => {
|
|
||||||
let regFolderName = new RegExp('^[a-z0-9][a-z0-9-]*[a-z0-9]$')
|
|
||||||
vueImage.newFolderName = _.kebabCase(_.trim(vueImage.newFolderName))
|
|
||||||
|
|
||||||
if (_.isEmpty(vueImage.newFolderName) || !regFolderName.test(vueImage.newFolderName)) {
|
|
||||||
vueImage.newFolderError = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vueImage.newFolderDiscard()
|
|
||||||
vueImage.isLoadingText = 'Creating new folder...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsCreateFolder', { foldername: vueImage.newFolderName }, (data) => {
|
|
||||||
vueImage.folders = data
|
|
||||||
vueImage.currentFolder = vueImage.newFolderName
|
|
||||||
vueImage.images = []
|
|
||||||
vueImage.isLoading = false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// FETCH FROM URL
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
fetchFromUrl: (ev) => {
|
|
||||||
vueImage.fetchFromUrlURL = ''
|
|
||||||
vueImage.fetchFromUrlShow = true
|
|
||||||
_.delay(() => { $('#txt-editor-image-fetchurl').focus() }, 400)
|
|
||||||
},
|
|
||||||
fetchFromUrlDiscard: (ev) => {
|
|
||||||
vueImage.fetchFromUrlShow = false
|
|
||||||
},
|
|
||||||
fetchFromUrlGo: (ev) => {
|
|
||||||
vueImage.fetchFromUrlDiscard()
|
|
||||||
vueImage.isLoadingText = 'Fetching image...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsFetchFileFromURL', { folder: vueImage.currentFolder, fetchUrl: vueImage.fetchFromUrlURL }, (data) => {
|
|
||||||
if (data.ok) {
|
|
||||||
vueImage.waitChangeComplete(vueImage.images.length, true)
|
|
||||||
} else {
|
|
||||||
vueImage.isLoading = false
|
|
||||||
alerts.pushError('Upload error', data.msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// RENAME IMAGE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
renameImage: () => {
|
|
||||||
let c = _.find(vueImage.images, [ '_id', vueImage.renameImageId ])
|
|
||||||
vueImage.renameImageFilename = c.basename || ''
|
|
||||||
vueImage.renameImageShow = true
|
|
||||||
_.delay(() => {
|
|
||||||
$('#txt-editor-image-rename').focus()
|
|
||||||
_.defer(() => { $('#txt-editor-image-rename').select() })
|
|
||||||
}, 400)
|
|
||||||
},
|
|
||||||
renameImageDiscard: () => {
|
|
||||||
vueImage.renameImageShow = false
|
|
||||||
},
|
|
||||||
renameImageGo: () => {
|
|
||||||
vueImage.renameImageDiscard()
|
|
||||||
vueImage.isLoadingText = 'Renaming image...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsRenameFile', { uid: vueImage.renameImageId, folder: vueImage.currentFolder, filename: vueImage.renameImageFilename }, (data) => {
|
|
||||||
if (data.ok) {
|
|
||||||
vueImage.waitChangeComplete(vueImage.images.length, false)
|
|
||||||
} else {
|
|
||||||
vueImage.isLoading = false
|
|
||||||
alerts.pushError('Rename error', data.msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// MOVE IMAGE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
moveImage: (uid, fld) => {
|
|
||||||
vueImage.isLoadingText = 'Moving image...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
|
|
||||||
if (data.ok) {
|
|
||||||
vueImage.loadImages()
|
|
||||||
} else {
|
|
||||||
vueImage.isLoading = false
|
|
||||||
alerts.pushError('Rename error', data.msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// DELETE IMAGE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
deleteImageWarn: (show) => {
|
|
||||||
if (show) {
|
|
||||||
let c = _.find(vueImage.images, [ '_id', vueImage.deleteImageId ])
|
|
||||||
vueImage.deleteImageFilename = c.filename || 'this image'
|
|
||||||
}
|
|
||||||
vueImage.deleteImageShow = show
|
|
||||||
},
|
|
||||||
deleteImageGo: () => {
|
|
||||||
vueImage.deleteImageWarn(false)
|
|
||||||
vueImage.isLoadingText = 'Deleting image...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsDeleteFile', { uid: vueImage.deleteImageId }, (data) => {
|
|
||||||
vueImage.loadImages()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// LOAD FROM REMOTE
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
selectFolder: (fldName) => {
|
|
||||||
vueImage.currentFolder = fldName
|
|
||||||
vueImage.loadImages()
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshFolders: () => {
|
|
||||||
vueImage.isLoadingText = 'Fetching folders list...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
vueImage.currentFolder = ''
|
|
||||||
vueImage.currentImage = ''
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsGetFolders', { }, (data) => {
|
|
||||||
vueImage.folders = data
|
|
||||||
vueImage.loadImages()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
loadImages: (silent) => {
|
|
||||||
if (!silent) {
|
|
||||||
vueImage.isLoadingText = 'Fetching images...'
|
|
||||||
vueImage.isLoading = true
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => {
|
|
||||||
vueImage.images = data
|
|
||||||
if (!silent) {
|
|
||||||
vueImage.isLoading = false
|
|
||||||
}
|
|
||||||
vueImage.attachContextMenus()
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
waitChangeComplete: (oldAmount, expectChange) => {
|
|
||||||
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
|
|
||||||
|
|
||||||
vueImage.postUploadChecks++
|
|
||||||
vueImage.isLoadingText = 'Processing...'
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
vueImage.loadImages(true).then(() => {
|
|
||||||
if ((vueImage.images.length !== oldAmount) === expectChange) {
|
|
||||||
vueImage.postUploadChecks = 0
|
|
||||||
vueImage.isLoading = false
|
|
||||||
} else if (vueImage.postUploadChecks > 5) {
|
|
||||||
vueImage.postUploadChecks = 0
|
|
||||||
vueImage.isLoading = false
|
|
||||||
alerts.pushError('Unable to fetch updated listing', 'Try again later')
|
|
||||||
} else {
|
|
||||||
_.delay(() => {
|
|
||||||
vueImage.waitChangeComplete(oldAmount, expectChange)
|
|
||||||
}, 1500)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// -------------------------------------------
|
|
||||||
// IMAGE CONTEXT MENU
|
|
||||||
// -------------------------------------------
|
|
||||||
|
|
||||||
attachContextMenus: () => {
|
|
||||||
let moveFolders = _.map(vueImage.folders, (f) => {
|
|
||||||
return {
|
|
||||||
name: (f !== '') ? f : '/ (root)',
|
|
||||||
icon: 'fa-folder',
|
|
||||||
callback: (key, opt) => {
|
|
||||||
let moveImageId = _.toString($(opt.$trigger).data('uid'))
|
|
||||||
let moveImageDestFolder = _.nth(vueImage.folders, key)
|
|
||||||
vueImage.moveImage(moveImageId, moveImageDestFolder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$.contextMenu('destroy', '.editor-modal-image-choices > figure')
|
|
||||||
$.contextMenu({
|
|
||||||
selector: '.editor-modal-image-choices > figure',
|
|
||||||
appendTo: '.editor-modal-image-choices',
|
|
||||||
position: (opt, x, y) => {
|
|
||||||
$(opt.$trigger).addClass('is-contextopen')
|
|
||||||
let trigPos = $(opt.$trigger).position()
|
|
||||||
let trigDim = { w: $(opt.$trigger).width() / 2, h: $(opt.$trigger).height() / 2 }
|
|
||||||
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
hide: (opt) => {
|
|
||||||
$(opt.$trigger).removeClass('is-contextopen')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
items: {
|
|
||||||
rename: {
|
|
||||||
name: 'Rename',
|
|
||||||
icon: 'fa-edit',
|
|
||||||
callback: (key, opt) => {
|
|
||||||
vueImage.renameImageId = _.toString(opt.$trigger[0].dataset.uid)
|
|
||||||
vueImage.renameImage()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
move: {
|
|
||||||
name: 'Move to...',
|
|
||||||
icon: 'fa-folder-open-o',
|
|
||||||
items: moveFolders
|
|
||||||
},
|
|
||||||
delete: {
|
|
||||||
name: 'Delete',
|
|
||||||
icon: 'fa-trash',
|
|
||||||
callback: (key, opt) => {
|
|
||||||
vueImage.deleteImageId = _.toString(opt.$trigger[0].dataset.uid)
|
|
||||||
vueImage.deleteImageWarn(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$('#btn-editor-image-upload input').on('change', (ev) => {
|
|
||||||
let curImageAmount = vueImage.images.length
|
|
||||||
|
|
||||||
$(ev.currentTarget).simpleUpload('/uploads/img', {
|
|
||||||
|
|
||||||
name: 'imgfile',
|
|
||||||
data: {
|
data: {
|
||||||
folder: vueImage.currentFolder
|
isLoading: false,
|
||||||
|
isLoadingText: '',
|
||||||
|
newFolderName: '',
|
||||||
|
newFolderShow: false,
|
||||||
|
newFolderError: false,
|
||||||
|
fetchFromUrlURL: '',
|
||||||
|
fetchFromUrlShow: false,
|
||||||
|
folders: [],
|
||||||
|
currentFolder: '',
|
||||||
|
currentImage: '',
|
||||||
|
currentAlign: 'left',
|
||||||
|
images: [],
|
||||||
|
uploadSucceeded: false,
|
||||||
|
postUploadChecks: 0,
|
||||||
|
renameImageShow: false,
|
||||||
|
renameImageId: '',
|
||||||
|
renameImageFilename: '',
|
||||||
|
deleteImageShow: false,
|
||||||
|
deleteImageId: '',
|
||||||
|
deleteImageFilename: ''
|
||||||
},
|
},
|
||||||
limit: 20,
|
methods: {
|
||||||
expect: 'json',
|
|
||||||
allowedExts: ['jpg', 'jpeg', 'gif', 'png', 'webp'],
|
|
||||||
allowedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
|
|
||||||
maxFileSize: 3145728, // max 3 MB
|
|
||||||
|
|
||||||
init: (totalUploads) => {
|
open: () => {
|
||||||
vueImage.uploadSucceeded = false
|
mdeModalOpenState = true
|
||||||
vueImage.isLoadingText = 'Preparing to upload...'
|
$('#modal-editor-image').addClass('is-active')
|
||||||
vueImage.isLoading = true
|
vueImage.refreshFolders()
|
||||||
},
|
},
|
||||||
|
cancel: (ev) => {
|
||||||
|
mdeModalOpenState = false
|
||||||
|
$('#modal-editor-image').removeClass('is-active')
|
||||||
|
},
|
||||||
|
|
||||||
progress: (progress) => {
|
// -------------------------------------------
|
||||||
vueImage.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
|
// INSERT IMAGE
|
||||||
},
|
// -------------------------------------------
|
||||||
|
|
||||||
success: (data) => {
|
selectImage: (imageId) => {
|
||||||
if (data.ok) {
|
vueImage.currentImage = imageId
|
||||||
let failedUpls = _.filter(data.results, ['ok', false])
|
},
|
||||||
if (failedUpls.length) {
|
insertImage: (ev) => {
|
||||||
_.forEach(failedUpls, (u) => {
|
if (mde.codemirror.doc.somethingSelected()) {
|
||||||
alerts.pushError('Upload error', u.msg)
|
mde.codemirror.execCommand('singleSelection')
|
||||||
|
}
|
||||||
|
|
||||||
|
let selImage = _.find(vueImage.images, ['_id', vueImage.currentImage])
|
||||||
|
selImage.normalizedPath = (selImage.folder === 'f:') ? selImage.filename : selImage.folder.slice(2) + '/' + selImage.filename
|
||||||
|
selImage.titleGuess = _.startCase(selImage.basename)
|
||||||
|
|
||||||
|
let imageText = '![' + selImage.titleGuess + '](/uploads/' + selImage.normalizedPath + ' "' + selImage.titleGuess + '")'
|
||||||
|
switch (vueImage.currentAlign) {
|
||||||
|
case 'center':
|
||||||
|
imageText += '{.align-center}'
|
||||||
|
break
|
||||||
|
case 'right':
|
||||||
|
imageText += '{.align-right}'
|
||||||
|
break
|
||||||
|
case 'logo':
|
||||||
|
imageText += '{.pagelogo}'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mde.codemirror.doc.replaceSelection(imageText)
|
||||||
|
vueImage.cancel()
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// NEW FOLDER
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
newFolder: (ev) => {
|
||||||
|
vueImage.newFolderName = ''
|
||||||
|
vueImage.newFolderError = false
|
||||||
|
vueImage.newFolderShow = true
|
||||||
|
_.delay(() => { $('#txt-editor-image-newfoldername').focus() }, 400)
|
||||||
|
},
|
||||||
|
newFolderDiscard: (ev) => {
|
||||||
|
vueImage.newFolderShow = false
|
||||||
|
},
|
||||||
|
newFolderCreate: (ev) => {
|
||||||
|
let regFolderName = new RegExp('^[a-z0-9][a-z0-9-]*[a-z0-9]$')
|
||||||
|
vueImage.newFolderName = _.kebabCase(_.trim(vueImage.newFolderName))
|
||||||
|
|
||||||
|
if (_.isEmpty(vueImage.newFolderName) || !regFolderName.test(vueImage.newFolderName)) {
|
||||||
|
vueImage.newFolderError = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vueImage.newFolderDiscard()
|
||||||
|
vueImage.isLoadingText = 'Creating new folder...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsCreateFolder', { foldername: vueImage.newFolderName }, (data) => {
|
||||||
|
vueImage.folders = data
|
||||||
|
vueImage.currentFolder = vueImage.newFolderName
|
||||||
|
vueImage.images = []
|
||||||
|
vueImage.isLoading = false
|
||||||
})
|
})
|
||||||
if (failedUpls.length < data.results.length) {
|
})
|
||||||
alerts.push({
|
},
|
||||||
title: 'Some uploads succeeded',
|
|
||||||
message: 'Files that are not mentionned in the errors above were uploaded successfully.'
|
// -------------------------------------------
|
||||||
|
// FETCH FROM URL
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
fetchFromUrl: (ev) => {
|
||||||
|
vueImage.fetchFromUrlURL = ''
|
||||||
|
vueImage.fetchFromUrlShow = true
|
||||||
|
_.delay(() => { $('#txt-editor-image-fetchurl').focus() }, 400)
|
||||||
|
},
|
||||||
|
fetchFromUrlDiscard: (ev) => {
|
||||||
|
vueImage.fetchFromUrlShow = false
|
||||||
|
},
|
||||||
|
fetchFromUrlGo: (ev) => {
|
||||||
|
vueImage.fetchFromUrlDiscard()
|
||||||
|
vueImage.isLoadingText = 'Fetching image...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsFetchFileFromURL', { folder: vueImage.currentFolder, fetchUrl: vueImage.fetchFromUrlURL }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
vueImage.waitChangeComplete(vueImage.images.length, true)
|
||||||
|
} else {
|
||||||
|
vueImage.isLoading = false
|
||||||
|
alerts.pushError('Upload error', data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// RENAME IMAGE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
renameImage: () => {
|
||||||
|
let c = _.find(vueImage.images, [ '_id', vueImage.renameImageId ])
|
||||||
|
vueImage.renameImageFilename = c.basename || ''
|
||||||
|
vueImage.renameImageShow = true
|
||||||
|
_.delay(() => {
|
||||||
|
$('#txt-editor-image-rename').focus()
|
||||||
|
_.defer(() => { $('#txt-editor-image-rename').select() })
|
||||||
|
}, 400)
|
||||||
|
},
|
||||||
|
renameImageDiscard: () => {
|
||||||
|
vueImage.renameImageShow = false
|
||||||
|
},
|
||||||
|
renameImageGo: () => {
|
||||||
|
vueImage.renameImageDiscard()
|
||||||
|
vueImage.isLoadingText = 'Renaming image...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsRenameFile', { uid: vueImage.renameImageId, folder: vueImage.currentFolder, filename: vueImage.renameImageFilename }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
vueImage.waitChangeComplete(vueImage.images.length, false)
|
||||||
|
} else {
|
||||||
|
vueImage.isLoading = false
|
||||||
|
alerts.pushError('Rename error', data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// MOVE IMAGE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
moveImage: (uid, fld) => {
|
||||||
|
vueImage.isLoadingText = 'Moving image...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
vueImage.loadImages()
|
||||||
|
} else {
|
||||||
|
vueImage.isLoading = false
|
||||||
|
alerts.pushError('Rename error', data.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// DELETE IMAGE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
deleteImageWarn: (show) => {
|
||||||
|
if (show) {
|
||||||
|
let c = _.find(vueImage.images, [ '_id', vueImage.deleteImageId ])
|
||||||
|
vueImage.deleteImageFilename = c.filename || 'this image'
|
||||||
|
}
|
||||||
|
vueImage.deleteImageShow = show
|
||||||
|
},
|
||||||
|
deleteImageGo: () => {
|
||||||
|
vueImage.deleteImageWarn(false)
|
||||||
|
vueImage.isLoadingText = 'Deleting image...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsDeleteFile', { uid: vueImage.deleteImageId }, (data) => {
|
||||||
|
vueImage.loadImages()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// LOAD FROM REMOTE
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
selectFolder: (fldName) => {
|
||||||
|
vueImage.currentFolder = fldName
|
||||||
|
vueImage.loadImages()
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshFolders: () => {
|
||||||
|
vueImage.isLoadingText = 'Fetching folders list...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
vueImage.currentFolder = ''
|
||||||
|
vueImage.currentImage = ''
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsGetFolders', { }, (data) => {
|
||||||
|
vueImage.folders = data
|
||||||
|
vueImage.loadImages()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
loadImages: (silent) => {
|
||||||
|
if (!silent) {
|
||||||
|
vueImage.isLoadingText = 'Fetching images...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => {
|
||||||
|
vueImage.images = data
|
||||||
|
if (!silent) {
|
||||||
|
vueImage.isLoading = false
|
||||||
|
}
|
||||||
|
vueImage.attachContextMenus()
|
||||||
|
resolve(true)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
waitChangeComplete: (oldAmount, expectChange) => {
|
||||||
|
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
|
||||||
|
|
||||||
|
vueImage.postUploadChecks++
|
||||||
|
vueImage.isLoadingText = 'Processing...'
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
vueImage.loadImages(true).then(() => {
|
||||||
|
if ((vueImage.images.length !== oldAmount) === expectChange) {
|
||||||
|
vueImage.postUploadChecks = 0
|
||||||
|
vueImage.isLoading = false
|
||||||
|
} else if (vueImage.postUploadChecks > 5) {
|
||||||
|
vueImage.postUploadChecks = 0
|
||||||
|
vueImage.isLoading = false
|
||||||
|
alerts.pushError('Unable to fetch updated listing', 'Try again later')
|
||||||
|
} else {
|
||||||
|
_.delay(() => {
|
||||||
|
vueImage.waitChangeComplete(oldAmount, expectChange)
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// -------------------------------------------
|
||||||
|
// IMAGE CONTEXT MENU
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
attachContextMenus: () => {
|
||||||
|
let moveFolders = _.map(vueImage.folders, (f) => {
|
||||||
|
return {
|
||||||
|
name: (f !== '') ? f : '/ (root)',
|
||||||
|
icon: 'fa-folder',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
let moveImageId = _.toString($(opt.$trigger).data('uid'))
|
||||||
|
let moveImageDestFolder = _.nth(vueImage.folders, key)
|
||||||
|
vueImage.moveImage(moveImageId, moveImageDestFolder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$.contextMenu('destroy', '.editor-modal-image-choices > figure')
|
||||||
|
$.contextMenu({
|
||||||
|
selector: '.editor-modal-image-choices > figure',
|
||||||
|
appendTo: '.editor-modal-image-choices',
|
||||||
|
position: (opt, x, y) => {
|
||||||
|
$(opt.$trigger).addClass('is-contextopen')
|
||||||
|
let trigPos = $(opt.$trigger).position()
|
||||||
|
let trigDim = { w: $(opt.$trigger).width() / 2, h: $(opt.$trigger).height() / 2 }
|
||||||
|
opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w })
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
hide: (opt) => {
|
||||||
|
$(opt.$trigger).removeClass('is-contextopen')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
rename: {
|
||||||
|
name: 'Rename',
|
||||||
|
icon: 'fa-edit',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
vueImage.renameImageId = _.toString(opt.$trigger[0].dataset.uid)
|
||||||
|
vueImage.renameImage()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move: {
|
||||||
|
name: 'Move to...',
|
||||||
|
icon: 'fa-folder-open-o',
|
||||||
|
items: moveFolders
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
name: 'Delete',
|
||||||
|
icon: 'fa-trash',
|
||||||
|
callback: (key, opt) => {
|
||||||
|
vueImage.deleteImageId = _.toString(opt.$trigger[0].dataset.uid)
|
||||||
|
vueImage.deleteImageWarn(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#btn-editor-image-upload input').on('change', (ev) => {
|
||||||
|
let curImageAmount = vueImage.images.length
|
||||||
|
|
||||||
|
$(ev.currentTarget).simpleUpload('/uploads/img', {
|
||||||
|
|
||||||
|
name: 'imgfile',
|
||||||
|
data: {
|
||||||
|
folder: vueImage.currentFolder
|
||||||
|
},
|
||||||
|
limit: 20,
|
||||||
|
expect: 'json',
|
||||||
|
allowedExts: ['jpg', 'jpeg', 'gif', 'png', 'webp'],
|
||||||
|
allowedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
|
||||||
|
maxFileSize: 3145728, // max 3 MB
|
||||||
|
|
||||||
|
init: (totalUploads) => {
|
||||||
|
vueImage.uploadSucceeded = false
|
||||||
|
vueImage.isLoadingText = 'Preparing to upload...'
|
||||||
|
vueImage.isLoading = true
|
||||||
|
},
|
||||||
|
|
||||||
|
progress: (progress) => {
|
||||||
|
vueImage.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
|
||||||
|
},
|
||||||
|
|
||||||
|
success: (data) => {
|
||||||
|
if (data.ok) {
|
||||||
|
let failedUpls = _.filter(data.results, ['ok', false])
|
||||||
|
if (failedUpls.length) {
|
||||||
|
_.forEach(failedUpls, (u) => {
|
||||||
|
alerts.pushError('Upload error', u.msg)
|
||||||
|
})
|
||||||
|
if (failedUpls.length < data.results.length) {
|
||||||
|
alerts.push({
|
||||||
|
title: 'Some uploads succeeded',
|
||||||
|
message: 'Files that are not mentionned in the errors above were uploaded successfully.'
|
||||||
|
})
|
||||||
|
vueImage.uploadSucceeded = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
vueImage.uploadSucceeded = true
|
vueImage.uploadSucceeded = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vueImage.uploadSucceeded = true
|
alerts.pushError('Upload error', data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
error: (error) => {
|
||||||
|
alerts.pushError(error.message, this.upload.file.name)
|
||||||
|
},
|
||||||
|
|
||||||
|
finish: () => {
|
||||||
|
if (vueImage.uploadSucceeded) {
|
||||||
|
vueImage.waitChangeComplete(curImageAmount, true)
|
||||||
|
} else {
|
||||||
|
vueImage.isLoading = false
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
alerts.pushError('Upload error', data.msg)
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
error: (error) => {
|
|
||||||
alerts.pushError(error.message, this.upload.file.name)
|
|
||||||
},
|
|
||||||
|
|
||||||
finish: () => {
|
|
||||||
if (vueImage.uploadSucceeded) {
|
|
||||||
vueImage.waitChangeComplete(curImageAmount, true)
|
|
||||||
} else {
|
|
||||||
vueImage.isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
return vueImage
|
||||||
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
/* global $, Vue, mde, _ */
|
'use strict'
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
const videoRules = {
|
const videoRules = {
|
||||||
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/, 'i'),
|
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/, 'i'),
|
||||||
@ -6,43 +10,46 @@ const videoRules = {
|
|||||||
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
|
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vue Video instance
|
module.exports = (mde, mdeModalOpenState) => {
|
||||||
|
// Vue Video instance
|
||||||
|
|
||||||
let vueVideo = new Vue({
|
let vueVideo = new Vue({
|
||||||
el: '#modal-editor-video',
|
el: '#modal-editor-video',
|
||||||
data: {
|
data: {
|
||||||
link: ''
|
link: ''
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
open: (ev) => {
|
|
||||||
$('#modal-editor-video').addClass('is-active')
|
|
||||||
$('#modal-editor-video input').focus()
|
|
||||||
},
|
},
|
||||||
cancel: (ev) => {
|
methods: {
|
||||||
mdeModalOpenState = false // eslint-disable-line no-undef
|
open: (ev) => {
|
||||||
$('#modal-editor-video').removeClass('is-active')
|
$('#modal-editor-video').addClass('is-active')
|
||||||
vueVideo.link = ''
|
$('#modal-editor-video input').focus()
|
||||||
},
|
},
|
||||||
insertVideo: (ev) => {
|
cancel: (ev) => {
|
||||||
if (mde.codemirror.doc.somethingSelected()) {
|
mdeModalOpenState = false // eslint-disable-line no-undef
|
||||||
mde.codemirror.execCommand('singleSelection')
|
$('#modal-editor-video').removeClass('is-active')
|
||||||
|
vueVideo.link = ''
|
||||||
|
},
|
||||||
|
insertVideo: (ev) => {
|
||||||
|
if (mde.codemirror.doc.somethingSelected()) {
|
||||||
|
mde.codemirror.execCommand('singleSelection')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guess video type
|
||||||
|
|
||||||
|
let videoType = _.findKey(videoRules, (vr) => {
|
||||||
|
return vr.test(vueVideo.link)
|
||||||
|
})
|
||||||
|
if (_.isNil(videoType)) {
|
||||||
|
videoType = 'video'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert video tag
|
||||||
|
|
||||||
|
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'
|
||||||
|
|
||||||
|
mde.codemirror.doc.replaceSelection(videoText)
|
||||||
|
vueVideo.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guess video type
|
|
||||||
|
|
||||||
let videoType = _.findKey(videoRules, (vr) => {
|
|
||||||
return vr.test(vueVideo.link)
|
|
||||||
})
|
|
||||||
if (_.isNil(videoType)) {
|
|
||||||
videoType = 'video'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert video tag
|
|
||||||
|
|
||||||
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'
|
|
||||||
|
|
||||||
mde.codemirror.doc.replaceSelection(videoText)
|
|
||||||
vueVideo.cancel()
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
return vueVideo
|
||||||
|
}
|
||||||
|
@ -1,220 +1,223 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/* global $, Vue, _, filesize, SimpleMDE, alerts, vueImage, vueFile, vueVideo, vueCodeBlock */
|
import $ from 'jquery'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import filesize from 'filesize.js'
|
||||||
|
import SimpleMDE from 'simplemde'
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Markdown Editor
|
// Markdown Editor
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
if ($('#mk-editor').length === 1) {
|
module.exports = (alerts, pageEntryPath, socket) => {
|
||||||
let mdeModalOpenState = false
|
if ($('#mk-editor').length === 1) {
|
||||||
let mdeCurrentEditor = null // eslint-disable-line no-unused-vars
|
Vue.filter('filesize', (v) => {
|
||||||
|
return _.toUpper(filesize(v))
|
||||||
|
})
|
||||||
|
|
||||||
Vue.filter('filesize', (v) => {
|
let mde
|
||||||
return _.toUpper(filesize(v))
|
let mdeModalOpenState = false
|
||||||
})
|
let vueImage = require('./editor-image.js')(alerts, mde, mdeModalOpenState, socket)
|
||||||
|
let vueFile = require('./editor-file.js')(alerts, mde, mdeModalOpenState, socket)
|
||||||
|
let vueVideo = require('./editor-video.js')(mde, mdeModalOpenState)
|
||||||
|
let vueCodeBlock = require('./editor-codeblock.js')(mde, mdeModalOpenState)
|
||||||
|
|
||||||
/* eslint-disable spaced-comment */
|
mde = new SimpleMDE({
|
||||||
//=include editor-image.js
|
autofocus: true,
|
||||||
//=include editor-file.js
|
autoDownloadFontAwesome: false,
|
||||||
//=include editor-video.js
|
element: $('#mk-editor').get(0),
|
||||||
//=include editor-codeblock.js
|
placeholder: 'Enter Markdown formatted content here...',
|
||||||
/* eslint-enable spaced-comment */
|
spellChecker: false,
|
||||||
|
status: false,
|
||||||
var mde = new SimpleMDE({
|
toolbar: [
|
||||||
autofocus: true,
|
{
|
||||||
autoDownloadFontAwesome: false,
|
name: 'bold',
|
||||||
element: $('#mk-editor').get(0),
|
action: SimpleMDE.toggleBold,
|
||||||
placeholder: 'Enter Markdown formatted content here...',
|
className: 'icon-bold',
|
||||||
spellChecker: false,
|
title: 'Bold'
|
||||||
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: 'italic',
|
||||||
},
|
action: SimpleMDE.toggleItalic,
|
||||||
{
|
className: 'icon-italic',
|
||||||
name: 'image',
|
title: 'Italic'
|
||||||
action: (editor) => {
|
|
||||||
if (!mdeModalOpenState) {
|
|
||||||
vueImage.open()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
className: 'icon-image',
|
{
|
||||||
title: 'Insert Image'
|
name: 'strikethrough',
|
||||||
},
|
action: SimpleMDE.toggleStrikethrough,
|
||||||
{
|
className: 'icon-strikethrough',
|
||||||
name: 'file',
|
title: 'Strikethrough'
|
||||||
action: (editor) => {
|
|
||||||
if (!mdeModalOpenState) {
|
|
||||||
vueFile.open()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
className: 'icon-paper',
|
'|',
|
||||||
title: 'Insert File'
|
{
|
||||||
},
|
name: 'heading-1',
|
||||||
{
|
action: SimpleMDE.toggleHeading1,
|
||||||
name: 'video',
|
className: 'icon-header fa-header-x fa-header-1',
|
||||||
action: (editor) => {
|
title: 'Big Heading'
|
||||||
if (!mdeModalOpenState) {
|
|
||||||
vueVideo.open()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
className: 'icon-video-camera2',
|
{
|
||||||
title: 'Insert Video Player'
|
name: 'heading-2',
|
||||||
},
|
action: SimpleMDE.toggleHeading2,
|
||||||
'|',
|
className: 'icon-header fa-header-x fa-header-2',
|
||||||
{
|
title: 'Medium Heading'
|
||||||
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: 'heading-3',
|
||||||
},
|
action: SimpleMDE.toggleHeading3,
|
||||||
{
|
className: 'icon-header fa-header-x fa-header-3',
|
||||||
name: 'code-block',
|
title: 'Small Heading'
|
||||||
action: (editor) => {
|
},
|
||||||
if (!mdeModalOpenState) {
|
{
|
||||||
mdeModalOpenState = true
|
name: 'quote',
|
||||||
|
action: SimpleMDE.toggleBlockquote,
|
||||||
if (mde.codemirror.doc.somethingSelected()) {
|
className: 'icon-quote-left',
|
||||||
vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
|
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) {
|
||||||
|
mdeModalOpenState = true
|
||||||
|
|
||||||
vueCodeBlock.open()
|
if (mde.codemirror.doc.somethingSelected()) {
|
||||||
}
|
vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
vueCodeBlock.open()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
className: 'icon-code',
|
||||||
|
title: 'Code Block'
|
||||||
},
|
},
|
||||||
className: 'icon-code',
|
'|',
|
||||||
title: 'Code Block'
|
{
|
||||||
},
|
name: 'table',
|
||||||
'|',
|
action: (editor) => {
|
||||||
{
|
window.alert('Coming soon!')
|
||||||
name: 'table',
|
// todo
|
||||||
action: (editor) => {
|
},
|
||||||
window.alert('Coming soon!')
|
className: 'icon-table',
|
||||||
// todo
|
title: 'Insert Table'
|
||||||
},
|
},
|
||||||
className: 'icon-table',
|
{
|
||||||
title: 'Insert Table'
|
name: 'horizontal-rule',
|
||||||
},
|
action: SimpleMDE.drawHorizontalRule,
|
||||||
{
|
className: 'icon-minus2',
|
||||||
name: 'horizontal-rule',
|
title: 'Horizontal Rule'
|
||||||
action: SimpleMDE.drawHorizontalRule,
|
}
|
||||||
className: 'icon-minus2',
|
],
|
||||||
title: 'Horizontal Rule'
|
shortcuts: {
|
||||||
|
'toggleBlockquote': null,
|
||||||
|
'toggleFullScreen': null
|
||||||
}
|
}
|
||||||
],
|
})
|
||||||
shortcuts: {
|
|
||||||
'toggleBlockquote': null,
|
// -> Save
|
||||||
'toggleFullScreen': null
|
|
||||||
|
let saveCurrentDocument = (ev) => {
|
||||||
|
$.ajax(window.location.href, {
|
||||||
|
data: {
|
||||||
|
markdown: mde.value()
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'PUT'
|
||||||
|
}).then((rData, rStatus, rXHR) => {
|
||||||
|
if (rData.ok) {
|
||||||
|
window.location.assign('/' + pageEntryPath) // eslint-disable-line no-undef
|
||||||
|
} else {
|
||||||
|
alerts.pushError('Something went wrong', rData.error)
|
||||||
|
}
|
||||||
|
}, (rXHR, rStatus, err) => {
|
||||||
|
alerts.pushError('Something went wrong', 'Save operation failed.')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// -> Save
|
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
|
||||||
|
saveCurrentDocument(ev)
|
||||||
|
})
|
||||||
|
|
||||||
let saveCurrentDocument = (ev) => {
|
$(window).bind('keydown', (ev) => {
|
||||||
$.ajax(window.location.href, {
|
if (ev.ctrlKey || ev.metaKey) {
|
||||||
data: {
|
switch (String.fromCharCode(ev.which).toLowerCase()) {
|
||||||
markdown: mde.value()
|
case 's':
|
||||||
},
|
ev.preventDefault()
|
||||||
dataType: 'json',
|
saveCurrentDocument(ev)
|
||||||
method: 'PUT'
|
break
|
||||||
}).then((rData, rStatus, rXHR) => {
|
}
|
||||||
if (rData.ok) {
|
|
||||||
window.location.assign('/' + pageEntryPath) // eslint-disable-line no-undef
|
|
||||||
} else {
|
|
||||||
alerts.pushError('Something went wrong', rData.error)
|
|
||||||
}
|
}
|
||||||
}, (rXHR, rStatus, err) => {
|
|
||||||
alerts.pushError('Something went wrong', 'Save operation failed.')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
|
|
||||||
saveCurrentDocument(ev)
|
|
||||||
})
|
|
||||||
|
|
||||||
$(window).bind('keydown', (ev) => {
|
|
||||||
if (ev.ctrlKey || ev.metaKey) {
|
|
||||||
switch (String.fromCharCode(ev.which).toLowerCase()) {
|
|
||||||
case 's':
|
|
||||||
ev.preventDefault()
|
|
||||||
saveCurrentDocument(ev)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -1,83 +1,87 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/* global $, Vue, _, socket */
|
import $ from 'jquery'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
if ($('#search-input').length) {
|
module.exports = (socket) => {
|
||||||
$('#search-input').focus()
|
if ($('#search-input').length) {
|
||||||
|
$('#search-input').focus()
|
||||||
|
|
||||||
$('.searchresults').css('display', 'block')
|
$('.searchresults').css('display', 'block')
|
||||||
|
|
||||||
var vueHeader = new Vue({
|
var vueHeader = new Vue({
|
||||||
el: '#header-container',
|
el: '#header-container',
|
||||||
data: {
|
data: {
|
||||||
searchq: '',
|
searchq: '',
|
||||||
searchres: [],
|
searchres: [],
|
||||||
searchsuggest: [],
|
searchsuggest: [],
|
||||||
searchload: 0,
|
searchload: 0,
|
||||||
searchactive: false,
|
searchactive: false,
|
||||||
searchmoveidx: 0,
|
searchmoveidx: 0,
|
||||||
searchmovekey: '',
|
searchmovekey: '',
|
||||||
searchmovearr: []
|
searchmovearr: []
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
searchq: (val, oldVal) => {
|
searchq: (val, oldVal) => {
|
||||||
vueHeader.searchmoveidx = 0
|
vueHeader.searchmoveidx = 0
|
||||||
if (val.length >= 3) {
|
if (val.length >= 3) {
|
||||||
vueHeader.searchactive = true
|
vueHeader.searchactive = true
|
||||||
vueHeader.searchload++
|
vueHeader.searchload++
|
||||||
socket.emit('search', { terms: val }, (data) => {
|
socket.emit('search', { terms: val }, (data) => {
|
||||||
vueHeader.searchres = data.match
|
vueHeader.searchres = data.match
|
||||||
vueHeader.searchsuggest = data.suggest
|
vueHeader.searchsuggest = data.suggest
|
||||||
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
|
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
|
||||||
if (vueHeader.searchload > 0) { vueHeader.searchload-- }
|
if (vueHeader.searchload > 0) { vueHeader.searchload-- }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
vueHeader.searchactive = false
|
vueHeader.searchactive = false
|
||||||
vueHeader.searchres = []
|
vueHeader.searchres = []
|
||||||
vueHeader.searchsuggest = []
|
vueHeader.searchsuggest = []
|
||||||
vueHeader.searchmovearr = []
|
vueHeader.searchmovearr = []
|
||||||
vueHeader.searchload = 0
|
vueHeader.searchload = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchmoveidx: (val, oldVal) => {
|
||||||
|
if (val > 0) {
|
||||||
|
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1])
|
||||||
|
? 'res.' + vueHeader.searchmovearr[val - 1].entryPath
|
||||||
|
: 'sug.' + vueHeader.searchmovearr[val - 1]
|
||||||
|
} else {
|
||||||
|
vueHeader.searchmovekey = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
searchmoveidx: (val, oldVal) => {
|
methods: {
|
||||||
if (val > 0) {
|
useSuggestion: (sug) => {
|
||||||
vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1])
|
vueHeader.searchq = sug
|
||||||
? 'res.' + vueHeader.searchmovearr[val - 1].entryPath
|
},
|
||||||
: 'sug.' + vueHeader.searchmovearr[val - 1]
|
closeSearch: () => {
|
||||||
} else {
|
vueHeader.searchq = ''
|
||||||
vueHeader.searchmovekey = ''
|
},
|
||||||
|
moveSelectSearch: () => {
|
||||||
|
if (vueHeader.searchmoveidx < 1) { return }
|
||||||
|
let i = vueHeader.searchmoveidx - 1
|
||||||
|
|
||||||
|
if (vueHeader.searchmovearr[i]) {
|
||||||
|
window.location.assign('/' + vueHeader.searchmovearr[i].entryPath)
|
||||||
|
} else {
|
||||||
|
vueHeader.searchq = vueHeader.searchmovearr[i]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveDownSearch: () => {
|
||||||
|
if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
|
||||||
|
vueHeader.searchmoveidx++
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveUpSearch: () => {
|
||||||
|
if (vueHeader.searchmoveidx > 0) {
|
||||||
|
vueHeader.searchmoveidx--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
methods: {
|
|
||||||
useSuggestion: (sug) => {
|
|
||||||
vueHeader.searchq = sug
|
|
||||||
},
|
|
||||||
closeSearch: () => {
|
|
||||||
vueHeader.searchq = ''
|
|
||||||
},
|
|
||||||
moveSelectSearch: () => {
|
|
||||||
if (vueHeader.searchmoveidx < 1) { return }
|
|
||||||
let i = vueHeader.searchmoveidx - 1
|
|
||||||
|
|
||||||
if (vueHeader.searchmovearr[i]) {
|
$('main').on('click', vueHeader.closeSearch)
|
||||||
window.location.assign('/' + vueHeader.searchmovearr[i].entryPath)
|
}
|
||||||
} else {
|
|
||||||
vueHeader.searchq = vueHeader.searchmovearr[i]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moveDownSearch: () => {
|
|
||||||
if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) {
|
|
||||||
vueHeader.searchmoveidx++
|
|
||||||
}
|
|
||||||
},
|
|
||||||
moveUpSearch: () => {
|
|
||||||
if (vueHeader.searchmoveidx > 0) {
|
|
||||||
vueHeader.searchmoveidx--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$('main').on('click', vueHeader.closeSearch)
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
/* eslint-disable no-unused-vars */
|
'use strict'
|
||||||
|
|
||||||
function setInputSelection (input, startPos, endPos) {
|
module.exports = {
|
||||||
input.focus()
|
/**
|
||||||
if (typeof input.selectionStart !== 'undefined') {
|
* Set Input Selection
|
||||||
input.selectionStart = startPos
|
* @param {DOMElement} input The input element
|
||||||
input.selectionEnd = endPos
|
* @param {number} startPos The starting position
|
||||||
} else if (document.selection && document.selection.createRange) {
|
* @param {nunber} endPos The ending position
|
||||||
// IE branch
|
*/
|
||||||
input.select()
|
setInputSelection: (input, startPos, endPos) => {
|
||||||
var range = document.selection.createRange()
|
input.focus()
|
||||||
range.collapse(true)
|
if (typeof input.selectionStart !== 'undefined') {
|
||||||
range.moveEnd('character', endPos)
|
input.selectionStart = startPos
|
||||||
range.moveStart('character', startPos)
|
input.selectionEnd = endPos
|
||||||
range.select()
|
} else if (document.selection && document.selection.createRange) {
|
||||||
|
// IE branch
|
||||||
|
input.select()
|
||||||
|
var range = document.selection.createRange()
|
||||||
|
range.collapse(true)
|
||||||
|
range.moveEnd('character', endPos)
|
||||||
|
range.moveStart('character', startPos)
|
||||||
|
range.select()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-enable no-unused-vars */
|
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
/* global _ */
|
'use strict'
|
||||||
/* eslint-disable no-unused-vars */
|
|
||||||
|
|
||||||
function makeSafePath (rawPath) {
|
import _ from 'lodash'
|
||||||
let rawParts = _.split(_.trim(rawPath), '/')
|
|
||||||
rawParts = _.map(rawParts, (r) => {
|
|
||||||
return _.kebabCase(_.deburr(_.trim(r)))
|
|
||||||
})
|
|
||||||
|
|
||||||
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Convert raw path to safe path
|
||||||
|
* @param {string} rawPath Raw path
|
||||||
|
* @returns {string} Safe path
|
||||||
|
*/
|
||||||
|
makeSafePath: (rawPath) => {
|
||||||
|
let rawParts = _.split(_.trim(rawPath), '/')
|
||||||
|
rawParts = _.map(rawParts, (r) => {
|
||||||
|
return _.kebabCase(_.deburr(_.trim(r)))
|
||||||
|
})
|
||||||
|
|
||||||
|
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-enable no-unused-vars */
|
|
||||||
|
@ -1,51 +1,56 @@
|
|||||||
/* global $, Vue, alerts */
|
'use strict'
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
// Vue Create User instance
|
// Vue Create User instance
|
||||||
|
|
||||||
let vueCreateUser = new Vue({
|
module.exports = (alerts) => {
|
||||||
el: '#modal-admin-users-create',
|
let vueCreateUser = new Vue({
|
||||||
data: {
|
el: '#modal-admin-users-create',
|
||||||
email: '',
|
data: {
|
||||||
provider: 'local',
|
email: '',
|
||||||
password: '',
|
provider: 'local',
|
||||||
name: '',
|
password: '',
|
||||||
loading: false
|
name: '',
|
||||||
},
|
loading: false
|
||||||
methods: {
|
|
||||||
open: (ev) => {
|
|
||||||
$('#modal-admin-users-create').addClass('is-active')
|
|
||||||
$('#modal-admin-users-create input').first().focus()
|
|
||||||
},
|
},
|
||||||
cancel: (ev) => {
|
methods: {
|
||||||
$('#modal-admin-users-create').removeClass('is-active')
|
open: (ev) => {
|
||||||
vueCreateUser.email = ''
|
$('#modal-admin-users-create').addClass('is-active')
|
||||||
vueCreateUser.provider = 'local'
|
$('#modal-admin-users-create input').first().focus()
|
||||||
},
|
},
|
||||||
create: (ev) => {
|
cancel: (ev) => {
|
||||||
vueCreateUser.loading = true
|
$('#modal-admin-users-create').removeClass('is-active')
|
||||||
$.ajax('/admin/users/create', {
|
vueCreateUser.email = ''
|
||||||
data: {
|
vueCreateUser.provider = 'local'
|
||||||
email: vueCreateUser.email,
|
},
|
||||||
provider: vueCreateUser.provider,
|
create: (ev) => {
|
||||||
password: vueCreateUser.password,
|
vueCreateUser.loading = true
|
||||||
name: vueCreateUser.name
|
$.ajax('/admin/users/create', {
|
||||||
},
|
data: {
|
||||||
dataType: 'json',
|
email: vueCreateUser.email,
|
||||||
method: 'POST'
|
provider: vueCreateUser.provider,
|
||||||
}).then((rData, rStatus, rXHR) => {
|
password: vueCreateUser.password,
|
||||||
vueCreateUser.loading = false
|
name: vueCreateUser.name
|
||||||
if (rData.ok) {
|
},
|
||||||
vueCreateUser.cancel()
|
dataType: 'json',
|
||||||
window.location.reload(true)
|
method: 'POST'
|
||||||
} else {
|
}).then((rData, rStatus, rXHR) => {
|
||||||
alerts.pushError('Something went wrong', rData.msg)
|
vueCreateUser.loading = false
|
||||||
}
|
if (rData.ok) {
|
||||||
}, (rXHR, rStatus, err) => {
|
vueCreateUser.cancel()
|
||||||
vueCreateUser.loading = false
|
window.location.reload(true)
|
||||||
alerts.pushError('Error', rXHR.responseJSON.msg)
|
} else {
|
||||||
})
|
alerts.pushError('Something went wrong', rData.msg)
|
||||||
|
}
|
||||||
|
}, (rXHR, rStatus, err) => {
|
||||||
|
vueCreateUser.loading = false
|
||||||
|
alerts.pushError('Error', rXHR.responseJSON.msg)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
$('.btn-create-prompt').on('click', vueCreateUser.open)
|
$('.btn-create-prompt').on('click', vueCreateUser.open)
|
||||||
|
}
|
||||||
|
@ -1,34 +1,43 @@
|
|||||||
/* global $, Vue, usrData, alerts */
|
'use strict'
|
||||||
|
|
||||||
|
/* global usrData */
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
// Vue Delete User instance
|
// Vue Delete User instance
|
||||||
|
|
||||||
let vueDeleteUser = new Vue({
|
module.exports = (alerts) => {
|
||||||
el: '#modal-admin-users-delete',
|
let vueDeleteUser = new Vue({
|
||||||
data: {
|
el: '#modal-admin-users-delete',
|
||||||
loading: false
|
data: {
|
||||||
},
|
loading: false
|
||||||
methods: {
|
|
||||||
open: (ev) => {
|
|
||||||
$('#modal-admin-users-delete').addClass('is-active')
|
|
||||||
},
|
},
|
||||||
cancel: (ev) => {
|
methods: {
|
||||||
$('#modal-admin-users-delete').removeClass('is-active')
|
open: (ev) => {
|
||||||
},
|
$('#modal-admin-users-delete').addClass('is-active')
|
||||||
deleteUser: (ev) => {
|
},
|
||||||
vueDeleteUser.loading = true
|
cancel: (ev) => {
|
||||||
$.ajax('/admin/users/' + usrData._id, {
|
$('#modal-admin-users-delete').removeClass('is-active')
|
||||||
dataType: 'json',
|
},
|
||||||
method: 'DELETE'
|
deleteUser: (ev) => {
|
||||||
}).then((rData, rStatus, rXHR) => {
|
vueDeleteUser.loading = true
|
||||||
vueDeleteUser.loading = false
|
$.ajax('/admin/users/' + usrData._id, {
|
||||||
vueDeleteUser.cancel()
|
dataType: 'json',
|
||||||
window.location.assign('/admin/users')
|
method: 'DELETE'
|
||||||
}, (rXHR, rStatus, err) => {
|
}).then((rData, rStatus, rXHR) => {
|
||||||
vueDeleteUser.loading = false
|
vueDeleteUser.loading = false
|
||||||
alerts.pushError('Error', rXHR.responseJSON.msg)
|
vueDeleteUser.cancel()
|
||||||
})
|
window.location.assign('/admin/users')
|
||||||
|
}, (rXHR, rStatus, err) => {
|
||||||
|
vueDeleteUser.loading = false
|
||||||
|
alerts.pushError('Error', rXHR.responseJSON.msg)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
$('.btn-deluser-prompt').on('click', vueDeleteUser.open)
|
$('.btn-deluser-prompt').on('click', vueDeleteUser.open)
|
||||||
|
}
|
||||||
|
@ -1,28 +1,35 @@
|
|||||||
/* global $, _, currentBasePath */
|
'use strict'
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { setInputSelection } from '../helpers/form'
|
||||||
|
import { makeSafePath } from '../helpers/pages'
|
||||||
|
|
||||||
// -> Create New Document
|
// -> Create New Document
|
||||||
|
|
||||||
let suggestedCreatePath = currentBasePath + '/new-page'
|
module.exports = (currentBasePath) => {
|
||||||
|
let suggestedCreatePath = currentBasePath + '/new-page'
|
||||||
|
|
||||||
$('.btn-create-prompt').on('click', (ev) => {
|
$('.btn-create-prompt').on('click', (ev) => {
|
||||||
$('#txt-create-prompt').val(suggestedCreatePath)
|
$('#txt-create-prompt').val(suggestedCreatePath)
|
||||||
$('#modal-create-prompt').toggleClass('is-active')
|
$('#modal-create-prompt').toggleClass('is-active')
|
||||||
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length) // eslint-disable-line no-undef
|
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length)
|
||||||
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
|
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#txt-create-prompt').on('keypress', (ev) => {
|
$('#txt-create-prompt').on('keypress', (ev) => {
|
||||||
if (ev.which === 13) {
|
if (ev.which === 13) {
|
||||||
$('.btn-create-go').trigger('click')
|
$('.btn-create-go').trigger('click')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
$('.btn-create-go').on('click', (ev) => {
|
$('.btn-create-go').on('click', (ev) => {
|
||||||
let newDocPath = makeSafePath($('#txt-create-prompt').val()) // eslint-disable-line no-undef
|
let newDocPath = makeSafePath($('#txt-create-prompt').val())
|
||||||
if (_.isEmpty(newDocPath)) {
|
if (_.isEmpty(newDocPath)) {
|
||||||
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
|
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
|
||||||
} else {
|
} else {
|
||||||
$('#txt-create-prompt').parent().addClass('is-loading')
|
$('#txt-create-prompt').parent().addClass('is-loading')
|
||||||
window.location.assign('/create/' + newDocPath)
|
window.location.assign('/create/' + newDocPath)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
@ -1,47 +1,54 @@
|
|||||||
/* global $, _, alerts, currentBasePath */
|
'use strict'
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { makeSafePath } from '../helpers/form'
|
||||||
|
import { setInputSelection } from '../helpers/pages'
|
||||||
|
|
||||||
// -> Move Existing Document
|
// -> Move Existing Document
|
||||||
|
|
||||||
if (currentBasePath !== '') {
|
module.exports = (currentBasePath, alerts) => {
|
||||||
$('.btn-move-prompt').removeClass('is-hidden')
|
if (currentBasePath !== '') {
|
||||||
|
$('.btn-move-prompt').removeClass('is-hidden')
|
||||||
|
}
|
||||||
|
|
||||||
|
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
|
||||||
|
|
||||||
|
$('.btn-move-prompt').on('click', (ev) => {
|
||||||
|
$('#txt-move-prompt').val(currentBasePath)
|
||||||
|
$('#modal-move-prompt').toggleClass('is-active')
|
||||||
|
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length)
|
||||||
|
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden')
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#txt-move-prompt').on('keypress', (ev) => {
|
||||||
|
if (ev.which === 13) {
|
||||||
|
$('.btn-move-go').trigger('click')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.btn-move-go').on('click', (ev) => {
|
||||||
|
let newDocPath = makeSafePath($('#txt-move-prompt').val())
|
||||||
|
if (_.isEmpty(newDocPath) || newDocPath === currentBasePath || newDocPath === 'home') {
|
||||||
|
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden')
|
||||||
|
} else {
|
||||||
|
$('#txt-move-prompt').parent().addClass('is-loading')
|
||||||
|
|
||||||
|
$.ajax(window.location.href, {
|
||||||
|
data: {
|
||||||
|
move: newDocPath
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'PUT'
|
||||||
|
}).then((rData, rStatus, rXHR) => {
|
||||||
|
if (rData.ok) {
|
||||||
|
window.location.assign('/' + newDocPath)
|
||||||
|
} else {
|
||||||
|
alerts.pushError('Something went wrong', rData.error)
|
||||||
|
}
|
||||||
|
}, (rXHR, rStatus, err) => {
|
||||||
|
alerts.pushError('Something went wrong', 'Save operation failed.')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
|
|
||||||
|
|
||||||
$('.btn-move-prompt').on('click', (ev) => {
|
|
||||||
$('#txt-move-prompt').val(currentBasePath)
|
|
||||||
$('#modal-move-prompt').toggleClass('is-active')
|
|
||||||
setInputSelection($('#txt-move-prompt').get(0), moveInitialDocument, currentBasePath.length) // eslint-disable-line no-undef
|
|
||||||
$('#txt-move-prompt').removeClass('is-danger').next().addClass('is-hidden')
|
|
||||||
})
|
|
||||||
|
|
||||||
$('#txt-move-prompt').on('keypress', (ev) => {
|
|
||||||
if (ev.which === 13) {
|
|
||||||
$('.btn-move-go').trigger('click')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$('.btn-move-go').on('click', (ev) => {
|
|
||||||
let newDocPath = makeSafePath($('#txt-move-prompt').val()) // eslint-disable-line no-undef
|
|
||||||
if (_.isEmpty(newDocPath) || newDocPath === currentBasePath || newDocPath === 'home') {
|
|
||||||
$('#txt-move-prompt').addClass('is-danger').next().removeClass('is-hidden')
|
|
||||||
} else {
|
|
||||||
$('#txt-move-prompt').parent().addClass('is-loading')
|
|
||||||
|
|
||||||
$.ajax(window.location.href, {
|
|
||||||
data: {
|
|
||||||
move: newDocPath
|
|
||||||
},
|
|
||||||
dataType: 'json',
|
|
||||||
method: 'PUT'
|
|
||||||
}).then((rData, rStatus, rXHR) => {
|
|
||||||
if (rData.ok) {
|
|
||||||
window.location.assign('/' + newDocPath)
|
|
||||||
} else {
|
|
||||||
alerts.pushError('Something went wrong', rData.error)
|
|
||||||
}
|
|
||||||
}, (rXHR, rStatus, err) => {
|
|
||||||
alerts.pushError('Something went wrong', 'Save operation failed.')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
@ -1,148 +1,149 @@
|
|||||||
/* global $, Vue, alerts, _, usrData, usrDataName */
|
'use strict'
|
||||||
|
|
||||||
if ($('#page-type-admin-profile').length) {
|
/* global usrData, usrDataName */
|
||||||
let vueProfile = new Vue({
|
|
||||||
el: '#page-type-admin-profile',
|
import $ from 'jquery'
|
||||||
data: {
|
import _ from 'lodash'
|
||||||
password: '********',
|
import Vue from 'vue'
|
||||||
passwordVerify: '********',
|
|
||||||
name: ''
|
module.exports = (alerts) => {
|
||||||
},
|
if ($('#page-type-admin-profile').length) {
|
||||||
methods: {
|
let vueProfile = new Vue({
|
||||||
saveUser: (ev) => {
|
el: '#page-type-admin-profile',
|
||||||
if (vueProfile.password !== vueProfile.passwordVerify) {
|
data: {
|
||||||
alerts.pushError('Error', "Passwords don't match!")
|
password: '********',
|
||||||
return
|
passwordVerify: '********',
|
||||||
|
name: ''
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
saveUser: (ev) => {
|
||||||
|
if (vueProfile.password !== vueProfile.passwordVerify) {
|
||||||
|
alerts.pushError('Error', "Passwords don't match!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$.post(window.location.href, {
|
||||||
|
password: vueProfile.password,
|
||||||
|
name: vueProfile.name
|
||||||
|
}).done((resp) => {
|
||||||
|
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
|
||||||
|
}).fail((jqXHR, txtStatus, resp) => {
|
||||||
|
alerts.pushError('Error', resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
$.post(window.location.href, {
|
},
|
||||||
password: vueProfile.password,
|
created: function () {
|
||||||
name: vueProfile.name
|
this.name = usrDataName
|
||||||
}).done((resp) => {
|
|
||||||
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
|
|
||||||
}).fail((jqXHR, txtStatus, resp) => {
|
|
||||||
alerts.pushError('Error', resp)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
created: function () {
|
} else if ($('#page-type-admin-users').length) {
|
||||||
this.name = usrDataName
|
require('../modals/admin-users-create.js')(alerts)
|
||||||
}
|
} else if ($('#page-type-admin-users-edit').length) {
|
||||||
})
|
let vueEditUser = new Vue({
|
||||||
} else if ($('#page-type-admin-users').length) {
|
el: '#page-type-admin-users-edit',
|
||||||
|
data: {
|
||||||
/* eslint-disable spaced-comment */
|
id: '',
|
||||||
//=include ../modals/admin-users-create.js
|
email: '',
|
||||||
/* eslint-enable spaced-comment */
|
password: '********',
|
||||||
|
name: '',
|
||||||
} else if ($('#page-type-admin-users-edit').length) {
|
rights: [],
|
||||||
let vueEditUser = new Vue({
|
roleoverride: 'none'
|
||||||
el: '#page-type-admin-users-edit',
|
|
||||||
data: {
|
|
||||||
id: '',
|
|
||||||
email: '',
|
|
||||||
password: '********',
|
|
||||||
name: '',
|
|
||||||
rights: [],
|
|
||||||
roleoverride: 'none'
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addRightsRow: (ev) => {
|
|
||||||
vueEditUser.rights.push({
|
|
||||||
role: 'write',
|
|
||||||
path: '/',
|
|
||||||
exact: false,
|
|
||||||
deny: false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
removeRightsRow: (idx) => {
|
methods: {
|
||||||
_.pullAt(vueEditUser.rights, idx)
|
addRightsRow: (ev) => {
|
||||||
vueEditUser.$forceUpdate()
|
vueEditUser.rights.push({
|
||||||
},
|
role: 'write',
|
||||||
saveUser: (ev) => {
|
path: '/',
|
||||||
let formattedRights = _.cloneDeep(vueEditUser.rights)
|
exact: false,
|
||||||
switch (vueEditUser.roleoverride) {
|
deny: false
|
||||||
case 'admin':
|
})
|
||||||
formattedRights.push({
|
},
|
||||||
role: 'admin',
|
removeRightsRow: (idx) => {
|
||||||
path: '/',
|
_.pullAt(vueEditUser.rights, idx)
|
||||||
exact: false,
|
vueEditUser.$forceUpdate()
|
||||||
deny: false
|
},
|
||||||
})
|
saveUser: (ev) => {
|
||||||
break
|
let formattedRights = _.cloneDeep(vueEditUser.rights)
|
||||||
|
switch (vueEditUser.roleoverride) {
|
||||||
|
case 'admin':
|
||||||
|
formattedRights.push({
|
||||||
|
role: 'admin',
|
||||||
|
path: '/',
|
||||||
|
exact: false,
|
||||||
|
deny: false
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
$.post(window.location.href, {
|
||||||
|
password: vueEditUser.password,
|
||||||
|
name: vueEditUser.name,
|
||||||
|
rights: JSON.stringify(formattedRights)
|
||||||
|
}).done((resp) => {
|
||||||
|
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
|
||||||
|
}).fail((jqXHR, txtStatus, resp) => {
|
||||||
|
alerts.pushError('Error', resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
$.post(window.location.href, {
|
},
|
||||||
password: vueEditUser.password,
|
created: function () {
|
||||||
name: vueEditUser.name,
|
this.id = usrData._id
|
||||||
rights: JSON.stringify(formattedRights)
|
this.email = usrData.email
|
||||||
}).done((resp) => {
|
this.name = usrData.name
|
||||||
alerts.pushSuccess('Saved successfully', 'Changes have been applied.')
|
|
||||||
}).fail((jqXHR, txtStatus, resp) => {
|
|
||||||
alerts.pushError('Error', resp)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created: function () {
|
|
||||||
this.id = usrData._id
|
|
||||||
this.email = usrData.email
|
|
||||||
this.name = usrData.name
|
|
||||||
|
|
||||||
if (_.find(usrData.rights, { role: 'admin' })) {
|
if (_.find(usrData.rights, { role: 'admin' })) {
|
||||||
this.rights = _.reject(usrData.rights, ['role', 'admin'])
|
this.rights = _.reject(usrData.rights, ['role', 'admin'])
|
||||||
this.roleoverride = 'admin'
|
this.roleoverride = 'admin'
|
||||||
} else {
|
} else {
|
||||||
this.rights = usrData.rights
|
this.rights = usrData.rights
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
require('../modals/admin-users-delete.js')(alerts)
|
||||||
|
} else if ($('#page-type-admin-settings').length) {
|
||||||
/* eslint-disable spaced-comment */
|
let vueSettings = new Vue({ // eslint-disable-line no-unused-vars
|
||||||
//=include ../modals/admin-users-delete.js
|
el: '#page-type-admin-settings',
|
||||||
/* eslint-enable spaced-comment */
|
data: {
|
||||||
} else if ($('#page-type-admin-settings').length) {
|
upgradeModal: {
|
||||||
let vueSettings = new Vue({ // eslint-disable-line no-unused-vars
|
state: false,
|
||||||
el: '#page-type-admin-settings',
|
step: 'confirm',
|
||||||
data: {
|
mode: 'upgrade',
|
||||||
upgradeModal: {
|
error: 'Something went wrong.'
|
||||||
state: false,
|
}
|
||||||
step: 'confirm',
|
},
|
||||||
mode: 'upgrade',
|
methods: {
|
||||||
error: 'Something went wrong.'
|
upgrade: (ev) => {
|
||||||
|
vueSettings.upgradeModal.mode = 'upgrade'
|
||||||
|
vueSettings.upgradeModal.step = 'confirm'
|
||||||
|
vueSettings.upgradeModal.state = true
|
||||||
|
},
|
||||||
|
reinstall: (ev) => {
|
||||||
|
vueSettings.upgradeModal.mode = 're-install'
|
||||||
|
vueSettings.upgradeModal.step = 'confirm'
|
||||||
|
vueSettings.upgradeModal.state = true
|
||||||
|
},
|
||||||
|
upgradeCancel: (ev) => {
|
||||||
|
vueSettings.upgradeModal.state = false
|
||||||
|
},
|
||||||
|
upgradeStart: (ev) => {
|
||||||
|
vueSettings.upgradeModal.step = 'running'
|
||||||
|
$.post('/admin/settings/install', {
|
||||||
|
mode: vueSettings.upgradeModal.mode
|
||||||
|
}).done((resp) => {
|
||||||
|
// todo
|
||||||
|
}).fail((jqXHR, txtStatus, resp) => {
|
||||||
|
vueSettings.upgradeModal.step = 'error'
|
||||||
|
vueSettings.upgradeModal.error = jqXHR.responseText
|
||||||
|
})
|
||||||
|
},
|
||||||
|
flushcache: (ev) => {
|
||||||
|
window.alert('Coming soon!')
|
||||||
|
},
|
||||||
|
resetaccounts: (ev) => {
|
||||||
|
window.alert('Coming soon!')
|
||||||
|
},
|
||||||
|
flushsessions: (ev) => {
|
||||||
|
window.alert('Coming soon!')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
methods: {
|
}
|
||||||
upgrade: (ev) => {
|
|
||||||
vueSettings.upgradeModal.mode = 'upgrade'
|
|
||||||
vueSettings.upgradeModal.step = 'confirm'
|
|
||||||
vueSettings.upgradeModal.state = true
|
|
||||||
},
|
|
||||||
reinstall: (ev) => {
|
|
||||||
vueSettings.upgradeModal.mode = 're-install'
|
|
||||||
vueSettings.upgradeModal.step = 'confirm'
|
|
||||||
vueSettings.upgradeModal.state = true
|
|
||||||
},
|
|
||||||
upgradeCancel: (ev) => {
|
|
||||||
vueSettings.upgradeModal.state = false
|
|
||||||
},
|
|
||||||
upgradeStart: (ev) => {
|
|
||||||
vueSettings.upgradeModal.step = 'running'
|
|
||||||
$.post('/admin/settings/install', {
|
|
||||||
mode: vueSettings.upgradeModal.mode
|
|
||||||
}).done((resp) => {
|
|
||||||
// todo
|
|
||||||
}).fail((jqXHR, txtStatus, resp) => {
|
|
||||||
vueSettings.upgradeModal.step = 'error'
|
|
||||||
vueSettings.upgradeModal.error = jqXHR.responseText
|
|
||||||
})
|
|
||||||
},
|
|
||||||
flushcache: (ev) => {
|
|
||||||
window.alert('Coming soon!')
|
|
||||||
},
|
|
||||||
resetaccounts: (ev) => {
|
|
||||||
window.alert('Coming soon!')
|
|
||||||
},
|
|
||||||
flushsessions: (ev) => {
|
|
||||||
window.alert('Coming soon!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
/* global $ */
|
'use strict'
|
||||||
|
|
||||||
if ($('#page-type-edit').length) {
|
import $ from 'jquery'
|
||||||
let pageEntryPath = $('#page-type-edit').data('entrypath') // eslint-disable-line no-unused-vars
|
|
||||||
// let pageCleanExit = false
|
|
||||||
|
|
||||||
// -> Discard
|
module.exports = (alerts, socket) => {
|
||||||
|
if ($('#page-type-edit').length) {
|
||||||
|
let pageEntryPath = $('#page-type-edit').data('entrypath')
|
||||||
|
// let pageCleanExit = false
|
||||||
|
|
||||||
$('.btn-edit-discard').on('click', (ev) => {
|
// -> Discard
|
||||||
$('#modal-edit-discard').toggleClass('is-active')
|
|
||||||
})
|
|
||||||
|
|
||||||
// window.onbeforeunload = function () {
|
$('.btn-edit-discard').on('click', (ev) => {
|
||||||
// return (pageCleanExit) ? true : 'Unsaved modifications will be lost. Are you sure you want to navigate away from this page?'
|
$('#modal-edit-discard').toggleClass('is-active')
|
||||||
// }
|
})
|
||||||
|
|
||||||
/* eslint-disable spaced-comment */
|
// window.onbeforeunload = function () {
|
||||||
//=include ../components/editor.js
|
// return (pageCleanExit) ? true : 'Unsaved modifications will be lost. Are you sure you want to navigate away from this page?'
|
||||||
/* eslint-enable spaced-comment */
|
// }
|
||||||
|
|
||||||
|
require('../components/editor.js')(alerts, pageEntryPath, socket)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
/* global $, ace */
|
'use strict'
|
||||||
|
|
||||||
if ($('#page-type-source').length) {
|
import $ from 'jquery'
|
||||||
var scEditor = ace.edit('source-display')
|
import * as ace from 'brace'
|
||||||
scEditor.setTheme('ace/theme/tomorrow_night')
|
import 'brace/theme/tomorrow_night'
|
||||||
scEditor.getSession().setMode('ace/mode/markdown')
|
import 'brace/mode/markdown'
|
||||||
scEditor.setOption('fontSize', '14px')
|
|
||||||
scEditor.setOption('hScrollBarAlwaysVisible', false)
|
|
||||||
scEditor.setOption('wrap', true)
|
|
||||||
scEditor.setReadOnly(true)
|
|
||||||
scEditor.renderer.updateFull()
|
|
||||||
|
|
||||||
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : '' // eslint-disable-line no-unused-vars
|
module.exports = (alerts) => {
|
||||||
|
if ($('#page-type-source').length) {
|
||||||
|
var scEditor = ace.edit('source-display')
|
||||||
|
scEditor.setTheme('ace/theme/tomorrow_night')
|
||||||
|
scEditor.getSession().setMode('ace/mode/markdown')
|
||||||
|
scEditor.setOption('fontSize', '14px')
|
||||||
|
scEditor.setOption('hScrollBarAlwaysVisible', false)
|
||||||
|
scEditor.setOption('wrap', true)
|
||||||
|
scEditor.setReadOnly(true)
|
||||||
|
scEditor.renderer.updateFull()
|
||||||
|
|
||||||
/* eslint-disable spaced-comment */
|
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : ''
|
||||||
//=include ../modals/create.js
|
|
||||||
//=include ../modals/move.js
|
require('../modals/create.js')(currentBasePath)
|
||||||
/* eslint-enable spaced-comment */
|
require('../modals/move.js')(currentBasePath, alerts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
/* global $ */
|
'use strict'
|
||||||
|
|
||||||
if ($('#page-type-view').length) {
|
import $ from 'jquery'
|
||||||
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : '' // eslint-disable-line no-unused-vars
|
|
||||||
|
|
||||||
/* eslint-disable spaced-comment */
|
module.exports = (alerts) => {
|
||||||
//=include ../modals/create.js
|
if ($('#page-type-view').length) {
|
||||||
//=include ../modals/move.js
|
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : ''
|
||||||
/* eslint-enable spaced-comment */
|
|
||||||
|
require('../modals/create.js')(currentBasePath)
|
||||||
|
require('../modals/move.js')(currentBasePath, alerts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ $primary: 'indigo';
|
|||||||
|
|
||||||
@import './libs/twemoji-awesome';
|
@import './libs/twemoji-awesome';
|
||||||
@import './libs/jquery-contextmenu';
|
@import './libs/jquery-contextmenu';
|
||||||
|
@import 'node_modules/highlight.js/styles/tomorrow';
|
||||||
|
@import 'node_modules/simplemde/dist/simplemde.min';
|
||||||
|
|
||||||
@import './components/_editor';
|
@import './components/_editor';
|
||||||
|
|
||||||
|
35
fuse.js
35
fuse.js
@ -27,6 +27,21 @@ const args = require('yargs')
|
|||||||
.alias('h', 'help')
|
.alias('h', 'help')
|
||||||
.argv
|
.argv
|
||||||
|
|
||||||
|
// Define aliases
|
||||||
|
|
||||||
|
const ALIASES = {
|
||||||
|
'ace': 'ace-builds/src-min-noconflict/ace.js',
|
||||||
|
'simplemde': 'simplemde/dist/simplemde.min.js',
|
||||||
|
'socket.io-client': 'socket.io-client/dist/socket.io.min.js',
|
||||||
|
'vue': 'vue/dist/vue.js'
|
||||||
|
}
|
||||||
|
const SHIMS = {
|
||||||
|
jquery: {
|
||||||
|
source: 'node_modules/jquery/dist/jquery.js',
|
||||||
|
exports: '$'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (args.d) {
|
if (args.d) {
|
||||||
// =============================================
|
// =============================================
|
||||||
// DEVELOPER MODE
|
// DEVELOPER MODE
|
||||||
@ -41,9 +56,8 @@ if (args.d) {
|
|||||||
const fuse = fsbx.FuseBox.init({
|
const fuse = fsbx.FuseBox.init({
|
||||||
homeDir: './client',
|
homeDir: './client',
|
||||||
outFile: './assets/js/bundle.min.js',
|
outFile: './assets/js/bundle.min.js',
|
||||||
alias: {
|
alias: ALIASES,
|
||||||
vue: 'vue/dist/vue.js'
|
shim: SHIMS,
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
|
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
|
||||||
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
|
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
|
||||||
@ -55,7 +69,8 @@ if (args.d) {
|
|||||||
|
|
||||||
fuse.devServer('>index.js', {
|
fuse.devServer('>index.js', {
|
||||||
port: 4444,
|
port: 4444,
|
||||||
httpServer: false
|
httpServer: false,
|
||||||
|
hmr: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// Server
|
// Server
|
||||||
@ -80,7 +95,7 @@ if (args.d) {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
} else if (args.c) {
|
} else if (args.c) {
|
||||||
// =============================================
|
// =============================================
|
||||||
// DEVELOPER MODE
|
// CONFIGURE - DEVELOPER MODE
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
console.info(colors.bgWhite.black(' Starting Fuse in CONFIGURE DEVELOPER mode... '))
|
console.info(colors.bgWhite.black(' Starting Fuse in CONFIGURE DEVELOPER mode... '))
|
||||||
@ -92,9 +107,8 @@ if (args.d) {
|
|||||||
const fuse = fsbx.FuseBox.init({
|
const fuse = fsbx.FuseBox.init({
|
||||||
homeDir: './client',
|
homeDir: './client',
|
||||||
outFile: './assets/js/configure.min.js',
|
outFile: './assets/js/configure.min.js',
|
||||||
alias: {
|
alias: ALIASES,
|
||||||
vue: 'vue/dist/vue.js'
|
shim: SHIMS,
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
|
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
|
||||||
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
|
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
|
||||||
@ -131,9 +145,8 @@ if (args.d) {
|
|||||||
|
|
||||||
const fuse = fsbx.FuseBox.init({
|
const fuse = fsbx.FuseBox.init({
|
||||||
homeDir: './client',
|
homeDir: './client',
|
||||||
alias: {
|
alias: ALIASES,
|
||||||
vue: 'vue/dist/vue.js'
|
shim: SHIMS,
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
[ fsbx.SassPlugin({ outputStyle: 'compressed', includePaths: ['./node_modules/requarks-core'] }), fsbx.CSSPlugin() ],
|
[ fsbx.SassPlugin({ outputStyle: 'compressed', includePaths: ['./node_modules/requarks-core'] }), fsbx.CSSPlugin() ],
|
||||||
fsbx.BabelPlugin({
|
fsbx.BabelPlugin({
|
||||||
|
26
package.json
26
package.json
@ -43,13 +43,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"auto-load": "^2.1.0",
|
"auto-load": "^2.1.0",
|
||||||
"axios": "^0.15.3",
|
"axios": "^0.16.0",
|
||||||
"bcryptjs-then": "^1.0.1",
|
"bcryptjs-then": "^1.0.1",
|
||||||
"bluebird": "^3.4.7",
|
"bluebird": "^3.4.7",
|
||||||
"body-parser": "^1.17.1",
|
"body-parser": "^1.17.1",
|
||||||
"bunyan": "^1.8.9",
|
"bunyan": "^1.8.9",
|
||||||
"cheerio": "^0.22.0",
|
"cheerio": "^0.22.0",
|
||||||
"child-process-promise": "^2.2.0",
|
"child-process-promise": "^2.2.1",
|
||||||
"chokidar": "^1.6.0",
|
"chokidar": "^1.6.0",
|
||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
"compression": "^1.6.2",
|
"compression": "^1.6.2",
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"express": "^4.15.2",
|
"express": "^4.15.2",
|
||||||
"express-brute": "^1.0.0",
|
"express-brute": "^1.0.0",
|
||||||
"express-brute-mongoose": "0.0.7",
|
"express-brute-mongoose": "0.0.7",
|
||||||
"express-session": "^1.15.1",
|
"express-session": "^1.15.2",
|
||||||
"file-type": "^4.0.0",
|
"file-type": "^4.0.0",
|
||||||
"filesize.js": "^1.0.2",
|
"filesize.js": "^1.0.2",
|
||||||
"follow-redirects": "^1.2.3",
|
"follow-redirects": "^1.2.3",
|
||||||
@ -85,19 +85,19 @@
|
|||||||
"markdown-it-expand-tabs": "^1.0.11",
|
"markdown-it-expand-tabs": "^1.0.11",
|
||||||
"markdown-it-external-links": "0.0.6",
|
"markdown-it-external-links": "0.0.6",
|
||||||
"markdown-it-footnote": "^3.0.1",
|
"markdown-it-footnote": "^3.0.1",
|
||||||
"markdown-it-task-lists": "^1.4.1",
|
"markdown-it-task-lists": "^2.0.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",
|
||||||
"moment-timezone": "^0.5.11",
|
"moment-timezone": "^0.5.11",
|
||||||
"mongodb": "^2.2.25",
|
"mongodb": "^2.2.25",
|
||||||
"mongoose": "^4.9.1",
|
"mongoose": "^4.9.2",
|
||||||
"multer": "^1.2.1",
|
"multer": "^1.2.1",
|
||||||
"ora": "^1.2.0",
|
"ora": "^1.2.0",
|
||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"passport.socketio": "^3.7.0",
|
"passport.socketio": "^3.7.0",
|
||||||
"pm2": "^2.4.2",
|
"pm2": "^2.4.3",
|
||||||
"pug": "^2.0.0-beta11",
|
"pug": "^2.0.0-beta11",
|
||||||
"read-chunk": "^2.0.0",
|
"read-chunk": "^2.0.0",
|
||||||
"remove-markdown": "^0.1.0",
|
"remove-markdown": "^0.1.0",
|
||||||
@ -119,33 +119,33 @@
|
|||||||
"winston": "^2.3.0"
|
"winston": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ace-builds": "^1.2.6",
|
|
||||||
"babel-cli": "^6.24.0",
|
"babel-cli": "^6.24.0",
|
||||||
"babel-jest": "^19.0.0",
|
"babel-jest": "^19.0.0",
|
||||||
"babel-preset-es2015": "^6.24.0",
|
"babel-preset-es2015": "^6.24.0",
|
||||||
|
"brace": "^0.10.0",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"eslint": "^3.18.0",
|
"eslint": "^3.19.0",
|
||||||
"eslint-config-standard": "^7.1.0",
|
"eslint-config-standard": "^7.1.0",
|
||||||
"eslint-plugin-import": "^2.2.0",
|
"eslint-plugin-import": "^2.2.0",
|
||||||
"eslint-plugin-node": "^4.2.1",
|
"eslint-plugin-node": "^4.2.2",
|
||||||
"eslint-plugin-promise": "^3.5.0",
|
"eslint-plugin-promise": "^3.5.0",
|
||||||
"eslint-plugin-standard": "^2.1.1",
|
"eslint-plugin-standard": "^2.1.1",
|
||||||
"fuse-box": "^1.3.128",
|
"fuse-box": "^1.3.129",
|
||||||
"jest": "^19.0.2",
|
"jest": "^19.0.2",
|
||||||
"jquery": "^3.2.1",
|
"jquery": "^3.2.1",
|
||||||
"jquery-contextmenu": "^2.4.4",
|
"jquery-contextmenu": "^2.4.4",
|
||||||
"jquery-simple-upload": "^1.0.0",
|
"jquery-simple-upload": "^1.0.0",
|
||||||
"jquery-smooth-scroll": "^2.0.0",
|
"jquery-smooth-scroll": "^2.0.0",
|
||||||
"node-sass": "^4.5.1",
|
"node-sass": "^4.5.2",
|
||||||
"nodemon": "^1.11.0",
|
"nodemon": "^1.11.0",
|
||||||
"pre-commit": "^1.2.2",
|
"pre-commit": "^1.2.2",
|
||||||
"pug-lint": "^2.4.0",
|
"pug-lint": "^2.4.0",
|
||||||
"snyk": "^1.25.1",
|
"snyk": "^1.26.1",
|
||||||
"standard": "^9.0.2",
|
"standard": "^9.0.2",
|
||||||
"sticky-js": "^1.1.9",
|
"sticky-js": "^1.1.9",
|
||||||
"twemoji-awesome": "^1.0.4",
|
"twemoji-awesome": "^1.0.4",
|
||||||
"vee-validate": "^2.0.0-beta.25",
|
"vee-validate": "^2.0.0-beta.25",
|
||||||
"vue": "^2.2.5"
|
"vue": "^2.2.6"
|
||||||
},
|
},
|
||||||
"standard": {
|
"standard": {
|
||||||
"globals": [
|
"globals": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user