Commit fe0c4ce0 authored by NGPixel's avatar NGPixel

Fuse-box client scripts integration + deps update

parent f6c519c5
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -8,8 +8,6 @@ switch (logic) {
require('./js/login.js')
break
default:
require('./node_modules/highlight.js/styles/tomorrow.css')
require('./node_modules/simplemde/dist/simplemde.min.css')
require('./scss/app.scss')
require('./js/app.js')
break
......
......@@ -2,14 +2,14 @@
/* global alertsData */
import jQuery from 'jquery'
import $ from 'jquery'
import _ from 'lodash'
import Sticky from 'sticky-js'
import io from 'socket.io-client'
import Alerts from './components/alerts.js'
/* eslint-disable spaced-comment */
import 'jquery-smooth-scroll'
import Sticky from 'sticky-js'
jQuery(document).ready(function ($) {
$(() => {
// ====================================
// Scroll
// ====================================
......@@ -45,24 +45,17 @@ jQuery(document).ready(function ($) {
// 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
// ====================================
//=include pages/view.js
//=include pages/create.js
//=include pages/edit.js
//=include pages/source.js
//=include pages/admin.js
require('./pages/view.js')(alerts)
// require('./pages/create.js')
require('./pages/edit.js')(alerts, socket)
require('./pages/source.js')(alerts)
require('./pages/admin.js')(alerts)
})
//=include helpers/form.js
//=include helpers/pages.js
//=include components/alerts.js
/* eslint-enable spaced-comment */
/* 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
// ACE - Mode Loader
......@@ -24,52 +30,56 @@ let loadAceMode = (m) => {
// Vue Code Block instance
let vueCodeBlock = new Vue({
el: '#modal-editor-codeblock',
data: {
modes: modelist.modesByName,
modeSelected: 'text',
initContent: ''
},
watch: {
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')
module.exports = (mde, mdeModalOpenState) => {
let modelist = {} // ace.require('ace/ext/modelist')
let vueCodeBlock = new Vue({
el: '#modal-editor-codeblock',
data: {
modes: modelist.modesByName,
modeSelected: 'text',
initContent: ''
},
watch: {
modeSelected: (val, oldVal) => {
loadAceMode(val).done(() => {
ace.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)
_.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.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'
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()
mde.codemirror.doc.replaceSelection(codeBlockText)
vueCodeBlock.cancel()
}
}
}
})
})
return vueCodeBlock
}
/* global $, Vue, _, alerts, mde, socket */
let vueFile = new Vue({
el: '#modal-editor-file',
data: {
isLoading: false,
isLoadingText: '',
newFolderName: '',
newFolderShow: false,
newFolderError: false,
folders: [],
currentFolder: '',
currentFile: '',
files: [],
uploadSucceeded: false,
postUploadChecks: 0,
renameFileShow: false,
renameFileId: '',
renameFileFilename: '',
deleteFileShow: false,
deleteFileId: '',
deleteFileFilename: ''
},
methods: {
open: () => {
mdeModalOpenState = true // eslint-disable-line no-undef
$('#modal-editor-file').addClass('is-active')
vueFile.refreshFolders()
},
cancel: (ev) => {
mdeModalOpenState = false // eslint-disable-line no-undef
$('#modal-editor-file').removeClass('is-active')
},
'use strict'
// -------------------------------------------
// INSERT LINK TO FILE
// -------------------------------------------
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
import 'jquery-contextmenu'
import 'jquery-simple-upload'
selectFile: (fileId) => {
vueFile.currentFile = fileId
module.exports = (alerts, mde, mdeModalOpenState, socket) => {
let vueFile = new Vue({
el: '#modal-editor-file',
data: {
isLoading: false,
isLoadingText: '',
newFolderName: '',
newFolderShow: false,
newFolderError: false,
folders: [],
currentFolder: '',
currentFile: '',
files: [],
uploadSucceeded: false,
postUploadChecks: 0,
renameFileShow: false,
renameFileId: '',
renameFileFilename: '',
deleteFileShow: false,
deleteFileId: '',
deleteFileFilename: ''
},
insertFileLink: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection')
}
methods: {
open: () => {
mdeModalOpenState = true // eslint-disable-line no-undef
$('#modal-editor-file').addClass('is-active')
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 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
}
let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")'
vueFile.newFolderDiscard()
vueFile.isLoadingText = 'Creating new folder...'
vueFile.isLoading = true
mde.codemirror.doc.replaceSelection(fileText)
vueFile.cancel()
},
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
// -------------------------------------------
// NEW FOLDER
// -------------------------------------------
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)
}
})
})
},
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))
// -------------------------------------------
// MOVE FILE
// -------------------------------------------
if (_.isEmpty(vueFile.newFolderName) || !regFolderName.test(vueFile.newFolderName)) {
vueFile.newFolderError = true
return
}
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)
}
})
})
},
vueFile.newFolderDiscard()
vueFile.isLoadingText = 'Creating new folder...'
vueFile.isLoading = true
// -------------------------------------------
// DELETE FILE
// -------------------------------------------
Vue.nextTick(() => {
socket.emit('uploadsCreateFolder', { foldername: vueFile.newFolderName }, (data) => {
vueFile.folders = data
vueFile.currentFolder = vueFile.newFolderName
vueFile.files = []
vueFile.isLoading = false
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()
})
})
})
},
},
// -------------------------------------------
// 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)
}
})
})
},
// -------------------------------------------
// LOAD FROM REMOTE
// -------------------------------------------
// -------------------------------------------
// MOVE FILE
// -------------------------------------------
selectFolder: (fldName) => {
vueFile.currentFolder = fldName
vueFile.loadFiles()
},
moveFile: (uid, fld) => {
vueFile.isLoadingText = 'Moving file...'
vueFile.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
if (data.ok) {
refreshFolders: () => {
vueFile.isLoadingText = 'Fetching folders list...'
vueFile.isLoading = true
vueFile.currentFolder = ''
vueFile.currentImage = ''
Vue.nextTick(() => {
socket.emit('uploadsGetFolders', { }, (data) => {
vueFile.folders = data
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()
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)
})
})
})
})
},
},
// -------------------------------------------
// LOAD FROM REMOTE
// -------------------------------------------
waitChangeComplete: (oldAmount, expectChange) => {
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
selectFolder: (fldName) => {
vueFile.currentFolder = fldName
vueFile.loadFiles()
},
vueFile.postUploadChecks++
vueFile.isLoadingText = 'Processing...'
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.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)
}
vueFile.attachContextMenus()
resolve(true)
})
})
})
},
},
waitChangeComplete: (oldAmount, expectChange) => {
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
// -------------------------------------------
// IMAGE CONTEXT MENU
// -------------------------------------------
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)
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)
}
}
})
})
},
// -------------------------------------------
// 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()
}
$.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 })
},
move: {
name: 'Move to...',
icon: 'fa-folder-open-o',
items: moveFolders
events: {
hide: (opt) => {
$(opt.$trigger).removeClass('is-contextopen')
}
},
delete: {
name: 'Delete',
icon: 'fa-trash',
callback: (key, opt) => {
vueFile.deleteFileId = _.toString(opt.$trigger[0].dataset.uid)
vueFile.deleteFileWarn(true)
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', {
$('#btn-editor-file-upload input').on('change', (ev) => {
let curFileAmount = vueFile.files.length
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
},
$(ev.currentTarget).simpleUpload('/uploads/file', {
progress: (progress) => {
vueFile.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
},
name: 'binfile',
data: {
folder: vueFile.currentFolder
},
limit: 20,
expect: 'json',
maxFileSize: 0,
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.'
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
}
} else {
vueFile.uploadSucceeded = true
alerts.pushError('Upload error', data.msg)
}
} else {
alerts.pushError('Upload error', data.msg)
}
},
},
error: (error) => {
alerts.pushError('Upload error', error.message)
},
error: (error) => {
alerts.pushError('Upload error', error.message)
},
finish: () => {
if (vueFile.uploadSucceeded) {
vueFile.waitChangeComplete(curFileAmount, true)
} else {
vueFile.isLoading = false
finish: () => {
if (vueFile.uploadSucceeded) {
vueFile.waitChangeComplete(curFileAmount, true)
} else {
vueFile.isLoading = false
}
}
}
})
})
})
return vueFile
}
/* global $, Vue, mde, _, alerts, socket */
let vueImage = new Vue({
el: '#modal-editor-image',
data: {
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: ''
},
methods: {
open: () => {
mdeModalOpenState = true // eslint-disable-line no-undef
$('#modal-editor-image').addClass('is-active')
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')
}
'use strict'
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
}
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
import 'jquery-contextmenu'
import 'jquery-simple-upload'
mde.codemirror.doc.replaceSelection(imageText)
vueImage.cancel()
module.exports = (alerts, mde, mdeModalOpenState, socket) => {
let vueImage = new Vue({
el: '#modal-editor-image',
data: {
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: ''
},
methods: {
open: () => {
mdeModalOpenState = true
$('#modal-editor-image').addClass('is-active')
vueImage.refreshFolders()
},
cancel: (ev) => {
mdeModalOpenState = false
$('#modal-editor-image').removeClass('is-active')
},
// -------------------------------------------
// INSERT IMAGE
// -------------------------------------------
selectImage: (imageId) => {
vueImage.currentImage = imageId
},
insertImage: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection')
}
// -------------------------------------------
// 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))
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
}
if (_.isEmpty(vueImage.newFolderName) || !regFolderName.test(vueImage.newFolderName)) {
vueImage.newFolderError = true
return
}
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
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
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
// -------------------------------------------
// FETCH FROM URL
// -------------------------------------------
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
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)
}
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)
}
})
})
})
},
},
// -------------------------------------------
// 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)
}
})
})
})
},
},
// -------------------------------------------
// MOVE IMAGE
// -------------------------------------------
// -------------------------------------------
// DELETE IMAGE
// -------------------------------------------
moveImage: (uid, fld) => {
vueImage.isLoadingText = 'Moving image...'
vueImage.isLoading = true
Vue.nextTick(() => {
socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
if (data.ok) {
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()
} else {
vueImage.isLoading = false
alerts.pushError('Rename error', data.msg)
}
})
})
})
},
},
// -------------------------------------------
// DELETE IMAGE
// -------------------------------------------
// -------------------------------------------
// LOAD FROM REMOTE
// -------------------------------------------
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()
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()
})
})
})
},
},
// -------------------------------------------
// LOAD FROM REMOTE
// -------------------------------------------
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)
})
})
})
},
selectFolder: (fldName) => {
vueImage.currentFolder = fldName
vueImage.loadImages()
},
waitChangeComplete: (oldAmount, expectChange) => {
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
refreshFolders: () => {
vueImage.isLoadingText = 'Fetching folders list...'
vueImage.isLoading = true
vueImage.currentFolder = ''
vueImage.currentImage = ''
Vue.nextTick(() => {
socket.emit('uploadsGetFolders', { }, (data) => {
vueImage.folders = data
vueImage.loadImages()
})
})
},
vueImage.postUploadChecks++
vueImage.isLoadingText = 'Processing...'
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.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)
}
vueImage.attachContextMenus()
resolve(true)
})
})
})
},
waitChangeComplete: (oldAmount, expectChange) => {
expectChange = (_.isBoolean(expectChange)) ? expectChange : true
},
vueImage.postUploadChecks++
vueImage.isLoadingText = 'Processing...'
// -------------------------------------------
// IMAGE CONTEXT MENU
// -------------------------------------------
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)
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)
}
}
})
})
},
// -------------------------------------------
// 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()
}
$.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 })
},
move: {
name: 'Move to...',
icon: 'fa-folder-open-o',
items: moveFolders
events: {
hide: (opt) => {
$(opt.$trigger).removeClass('is-contextopen')
}
},
delete: {
name: 'Delete',
icon: 'fa-trash',
callback: (key, opt) => {
vueImage.deleteImageId = _.toString(opt.$trigger[0].dataset.uid)
vueImage.deleteImageWarn(true)
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', {
$('#btn-editor-image-upload input').on('change', (ev) => {
let curImageAmount = vueImage.images.length
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
},
$(ev.currentTarget).simpleUpload('/uploads/img', {
progress: (progress) => {
vueImage.isLoadingText = 'Uploading...' + Math.round(progress) + '%'
},
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
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.'
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
}
} else {
vueImage.uploadSucceeded = true
alerts.pushError('Upload error', data.msg)
}
} else {
alerts.pushError('Upload error', data.msg)
}
},
},
error: (error) => {
alerts.pushError(error.message, this.upload.file.name)
},
error: (error) => {
alerts.pushError(error.message, this.upload.file.name)
},
finish: () => {
if (vueImage.uploadSucceeded) {
vueImage.waitChangeComplete(curImageAmount, true)
} else {
vueImage.isLoading = false
finish: () => {
if (vueImage.uploadSucceeded) {
vueImage.waitChangeComplete(curImageAmount, true)
} else {
vueImage.isLoading = false
}
}
}
})
})
})
return vueImage
}
/* global $, Vue, mde, _ */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
import _ from 'lodash'
const videoRules = {
'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')
}
// Vue Video instance
let vueVideo = new Vue({
el: '#modal-editor-video',
data: {
link: ''
},
methods: {
open: (ev) => {
$('#modal-editor-video').addClass('is-active')
$('#modal-editor-video input').focus()
},
cancel: (ev) => {
mdeModalOpenState = false // eslint-disable-line no-undef
$('#modal-editor-video').removeClass('is-active')
vueVideo.link = ''
},
insertVideo: (ev) => {
if (mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection')
}
// Guess video type
module.exports = (mde, mdeModalOpenState) => {
// Vue Video instance
let videoType = _.findKey(videoRules, (vr) => {
return vr.test(vueVideo.link)
})
if (_.isNil(videoType)) {
videoType = 'video'
let vueVideo = new Vue({
el: '#modal-editor-video',
data: {
link: ''
},
methods: {
open: (ev) => {
$('#modal-editor-video').addClass('is-active')
$('#modal-editor-video input').focus()
},
cancel: (ev) => {
mdeModalOpenState = false // eslint-disable-line no-undef
$('#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()
}
// Insert video tag
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n'
mde.codemirror.doc.replaceSelection(videoText)
vueVideo.cancel()
}
}
})
})
return vueVideo
}
'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
// ====================================
if ($('#mk-editor').length === 1) {
let mdeModalOpenState = false
let mdeCurrentEditor = null // eslint-disable-line no-unused-vars
module.exports = (alerts, pageEntryPath, socket) => {
if ($('#mk-editor').length === 1) {
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v))
})
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v))
})
let mde
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 */
//=include editor-image.js
//=include editor-file.js
//=include editor-video.js
//=include editor-codeblock.js
/* eslint-enable spaced-comment */
mde = new SimpleMDE({
autofocus: true,
autoDownloadFontAwesome: false,
element: $('#mk-editor').get(0),
placeholder: 'Enter Markdown formatted content here...',
spellChecker: false,
status: false,
toolbar: [
{
name: 'bold',
action: SimpleMDE.toggleBold,
className: 'icon-bold',
title: 'Bold'
},
{
name: 'italic',
action: SimpleMDE.toggleItalic,
className: 'icon-italic',
title: 'Italic'
},
{
name: 'strikethrough',
action: SimpleMDE.toggleStrikethrough,
className: 'icon-strikethrough',
title: 'Strikethrough'
},
'|',
{
name: 'heading-1',
action: SimpleMDE.toggleHeading1,
className: 'icon-header fa-header-x fa-header-1',
title: 'Big Heading'
},
{
name: 'heading-2',
action: SimpleMDE.toggleHeading2,
className: 'icon-header fa-header-x fa-header-2',
title: 'Medium Heading'
},
{
name: 'heading-3',
action: SimpleMDE.toggleHeading3,
className: 'icon-header fa-header-x fa-header-3',
title: 'Small Heading'
},
{
name: 'quote',
action: SimpleMDE.toggleBlockquote,
className: 'icon-quote-left',
title: 'Quote'
},
'|',
{
name: 'unordered-list',
action: SimpleMDE.toggleUnorderedList,
className: 'icon-th-list',
title: 'Bullet List'
},
{
name: 'ordered-list',
action: SimpleMDE.toggleOrderedList,
className: 'icon-list-ol',
title: 'Numbered List'
},
'|',
{
name: 'link',
action: (editor) => {
/* if(!mdeModalOpenState) {
mdeModalOpenState = true;
$('#modal-editor-link').slideToggle();
} */
window.alert('Coming soon!')
},
className: 'icon-link2',
title: 'Insert Link'
},
{
name: 'image',
action: (editor) => {
if (!mdeModalOpenState) {
vueImage.open()
}
},
className: 'icon-image',
title: 'Insert Image'
},
{
name: 'file',
action: (editor) => {
if (!mdeModalOpenState) {
vueFile.open()
}
},
className: 'icon-paper',
title: 'Insert File'
},
{
name: 'video',
action: (editor) => {
if (!mdeModalOpenState) {
vueVideo.open()
}
},
className: 'icon-video-camera2',
title: 'Insert Video Player'
},
'|',
{
name: 'inline-code',
action: (editor) => {
if (!editor.codemirror.doc.somethingSelected()) {
return alerts.pushError('Invalid selection', 'You must select at least 1 character first.')
}
let curSel = editor.codemirror.doc.getSelections()
curSel = _.map(curSel, (s) => {
return '`' + s + '`'
})
editor.codemirror.doc.replaceSelections(curSel)
},
className: 'icon-terminal',
title: 'Inline Code'
},
{
name: 'code-block',
action: (editor) => {
if (!mdeModalOpenState) {
mdeModalOpenState = true
var mde = new SimpleMDE({
autofocus: true,
autoDownloadFontAwesome: false,
element: $('#mk-editor').get(0),
placeholder: 'Enter Markdown formatted content here...',
spellChecker: false,
status: false,
toolbar: [
{
name: 'bold',
action: SimpleMDE.toggleBold,
className: 'icon-bold',
title: 'Bold'
},
{
name: 'italic',
action: SimpleMDE.toggleItalic,
className: 'icon-italic',
title: 'Italic'
},
{
name: 'strikethrough',
action: SimpleMDE.toggleStrikethrough,
className: 'icon-strikethrough',
title: 'Strikethrough'
},
'|',
{
name: 'heading-1',
action: SimpleMDE.toggleHeading1,
className: 'icon-header fa-header-x fa-header-1',
title: 'Big Heading'
},
{
name: 'heading-2',
action: SimpleMDE.toggleHeading2,
className: 'icon-header fa-header-x fa-header-2',
title: 'Medium Heading'
},
{
name: 'heading-3',
action: SimpleMDE.toggleHeading3,
className: 'icon-header fa-header-x fa-header-3',
title: 'Small Heading'
},
{
name: 'quote',
action: SimpleMDE.toggleBlockquote,
className: 'icon-quote-left',
title: 'Quote'
},
'|',
{
name: 'unordered-list',
action: SimpleMDE.toggleUnorderedList,
className: 'icon-th-list',
title: 'Bullet List'
},
{
name: 'ordered-list',
action: SimpleMDE.toggleOrderedList,
className: 'icon-list-ol',
title: 'Numbered List'
},
'|',
{
name: 'link',
action: (editor) => {
/* if(!mdeModalOpenState) {
mdeModalOpenState = true;
$('#modal-editor-link').slideToggle();
} */
window.alert('Coming soon!')
},
className: 'icon-link2',
title: 'Insert Link'
},
{
name: 'image',
action: (editor) => {
if (!mdeModalOpenState) {
vueImage.open()
}
},
className: 'icon-image',
title: 'Insert Image'
},
{
name: 'file',
action: (editor) => {
if (!mdeModalOpenState) {
vueFile.open()
}
},
className: 'icon-paper',
title: 'Insert File'
},
{
name: 'video',
action: (editor) => {
if (!mdeModalOpenState) {
vueVideo.open()
}
},
className: 'icon-video-camera2',
title: 'Insert Video Player'
},
'|',
{
name: 'inline-code',
action: (editor) => {
if (!editor.codemirror.doc.somethingSelected()) {
return alerts.pushError('Invalid selection', 'You must select at least 1 character first.')
}
let curSel = editor.codemirror.doc.getSelections()
curSel = _.map(curSel, (s) => {
return '`' + s + '`'
})
editor.codemirror.doc.replaceSelections(curSel)
},
className: 'icon-terminal',
title: 'Inline Code'
},
{
name: 'code-block',
action: (editor) => {
if (!mdeModalOpenState) {
mdeModalOpenState = true
if (mde.codemirror.doc.somethingSelected()) {
vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
}
if (mde.codemirror.doc.somethingSelected()) {
vueCodeBlock.initContent = mde.codemirror.doc.getSelection()
vueCodeBlock.open()
}
vueCodeBlock.open()
}
},
className: 'icon-code',
title: 'Code Block'
},
'|',
{
name: 'table',
action: (editor) => {
window.alert('Coming soon!')
// todo
},
className: 'icon-table',
title: 'Insert Table'
},
{
name: 'horizontal-rule',
action: SimpleMDE.drawHorizontalRule,
className: 'icon-minus2',
title: 'Horizontal Rule'
},
className: 'icon-code',
title: 'Code Block'
},
'|',
{
name: 'table',
action: (editor) => {
window.alert('Coming soon!')
// todo
},
className: 'icon-table',
title: 'Insert Table'
},
{
name: 'horizontal-rule',
action: SimpleMDE.drawHorizontalRule,
className: 'icon-minus2',
title: 'Horizontal Rule'
}
],
shortcuts: {
'toggleBlockquote': null,
'toggleFullScreen': null
}
],
shortcuts: {
'toggleBlockquote': null,
'toggleFullScreen': null
})
// -> Save
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) => {
$.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)
$(window).bind('keydown', (ev) => {
if (ev.ctrlKey || ev.metaKey) {
switch (String.fromCharCode(ev.which).toLowerCase()) {
case 's':
ev.preventDefault()
saveCurrentDocument(ev)
break
}
}
}, (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
}
}
})
}
'use strict'
/* global $, Vue, _, socket */
import $ from 'jquery'
import _ from 'lodash'
import Vue from 'vue'
if ($('#search-input').length) {
$('#search-input').focus()
module.exports = (socket) => {
if ($('#search-input').length) {
$('#search-input').focus()
$('.searchresults').css('display', 'block')
$('.searchresults').css('display', 'block')
var vueHeader = new Vue({
el: '#header-container',
data: {
searchq: '',
searchres: [],
searchsuggest: [],
searchload: 0,
searchactive: false,
searchmoveidx: 0,
searchmovekey: '',
searchmovearr: []
},
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0
if (val.length >= 3) {
vueHeader.searchactive = true
vueHeader.searchload++
socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match
vueHeader.searchsuggest = data.suggest
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
if (vueHeader.searchload > 0) { vueHeader.searchload-- }
})
} else {
vueHeader.searchactive = false
vueHeader.searchres = []
vueHeader.searchsuggest = []
vueHeader.searchmovearr = []
vueHeader.searchload = 0
}
var vueHeader = new Vue({
el: '#header-container',
data: {
searchq: '',
searchres: [],
searchsuggest: [],
searchload: 0,
searchactive: false,
searchmoveidx: 0,
searchmovekey: '',
searchmovearr: []
},
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 = ''
watch: {
searchq: (val, oldVal) => {
vueHeader.searchmoveidx = 0
if (val.length >= 3) {
vueHeader.searchactive = true
vueHeader.searchload++
socket.emit('search', { terms: val }, (data) => {
vueHeader.searchres = data.match
vueHeader.searchsuggest = data.suggest
vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest)
if (vueHeader.searchload > 0) { vueHeader.searchload-- }
})
} else {
vueHeader.searchactive = false
vueHeader.searchres = []
vueHeader.searchsuggest = []
vueHeader.searchmovearr = []
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 = ''
}
}
}
},
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug
},
closeSearch: () => {
vueHeader.searchq = ''
},
moveSelectSearch: () => {
if (vueHeader.searchmoveidx < 1) { return }
let i = vueHeader.searchmoveidx - 1
methods: {
useSuggestion: (sug) => {
vueHeader.searchq = sug
},
closeSearch: () => {
vueHeader.searchq = ''
},
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--
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--
}
}
}
}
})
})
$('main').on('click', vueHeader.closeSearch)
$('main').on('click', vueHeader.closeSearch)
}
}
/* eslint-disable no-unused-vars */
'use strict'
function setInputSelection (input, startPos, endPos) {
input.focus()
if (typeof input.selectionStart !== 'undefined') {
input.selectionStart = startPos
input.selectionEnd = endPos
} 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()
module.exports = {
/**
* Set Input Selection
* @param {DOMElement} input The input element
* @param {number} startPos The starting position
* @param {nunber} endPos The ending position
*/
setInputSelection: (input, startPos, endPos) => {
input.focus()
if (typeof input.selectionStart !== 'undefined') {
input.selectionStart = startPos
input.selectionEnd = endPos
} 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 */
/* global _ */
/* eslint-disable no-unused-vars */
'use strict'
function makeSafePath (rawPath) {
let rawParts = _.split(_.trim(rawPath), '/')
rawParts = _.map(rawParts, (r) => {
return _.kebabCase(_.deburr(_.trim(r)))
})
import _ from 'lodash'
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)))
})
/* eslint-enable no-unused-vars */
return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
}
}
/* global $, Vue, alerts */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
// Vue Create User instance
let vueCreateUser = new Vue({
el: '#modal-admin-users-create',
data: {
email: '',
provider: 'local',
password: '',
name: '',
loading: false
},
methods: {
open: (ev) => {
$('#modal-admin-users-create').addClass('is-active')
$('#modal-admin-users-create input').first().focus()
},
cancel: (ev) => {
$('#modal-admin-users-create').removeClass('is-active')
vueCreateUser.email = ''
vueCreateUser.provider = 'local'
module.exports = (alerts) => {
let vueCreateUser = new Vue({
el: '#modal-admin-users-create',
data: {
email: '',
provider: 'local',
password: '',
name: '',
loading: false
},
create: (ev) => {
vueCreateUser.loading = true
$.ajax('/admin/users/create', {
data: {
email: vueCreateUser.email,
provider: vueCreateUser.provider,
password: vueCreateUser.password,
name: vueCreateUser.name
},
dataType: 'json',
method: 'POST'
}).then((rData, rStatus, rXHR) => {
vueCreateUser.loading = false
if (rData.ok) {
vueCreateUser.cancel()
window.location.reload(true)
} else {
alerts.pushError('Something went wrong', rData.msg)
}
}, (rXHR, rStatus, err) => {
vueCreateUser.loading = false
alerts.pushError('Error', rXHR.responseJSON.msg)
})
methods: {
open: (ev) => {
$('#modal-admin-users-create').addClass('is-active')
$('#modal-admin-users-create input').first().focus()
},
cancel: (ev) => {
$('#modal-admin-users-create').removeClass('is-active')
vueCreateUser.email = ''
vueCreateUser.provider = 'local'
},
create: (ev) => {
vueCreateUser.loading = true
$.ajax('/admin/users/create', {
data: {
email: vueCreateUser.email,
provider: vueCreateUser.provider,
password: vueCreateUser.password,
name: vueCreateUser.name
},
dataType: 'json',
method: 'POST'
}).then((rData, rStatus, rXHR) => {
vueCreateUser.loading = false
if (rData.ok) {
vueCreateUser.cancel()
window.location.reload(true)
} 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)
}
/* global $, Vue, usrData, alerts */
'use strict'
/* global usrData */
'use strict'
import $ from 'jquery'
import Vue from 'vue'
// Vue Delete User instance
let vueDeleteUser = new Vue({
el: '#modal-admin-users-delete',
data: {
loading: false
},
methods: {
open: (ev) => {
$('#modal-admin-users-delete').addClass('is-active')
},
cancel: (ev) => {
$('#modal-admin-users-delete').removeClass('is-active')
module.exports = (alerts) => {
let vueDeleteUser = new Vue({
el: '#modal-admin-users-delete',
data: {
loading: false
},
deleteUser: (ev) => {
vueDeleteUser.loading = true
$.ajax('/admin/users/' + usrData._id, {
dataType: 'json',
method: 'DELETE'
}).then((rData, rStatus, rXHR) => {
vueDeleteUser.loading = false
vueDeleteUser.cancel()
window.location.assign('/admin/users')
}, (rXHR, rStatus, err) => {
vueDeleteUser.loading = false
alerts.pushError('Error', rXHR.responseJSON.msg)
})
methods: {
open: (ev) => {
$('#modal-admin-users-delete').addClass('is-active')
},
cancel: (ev) => {
$('#modal-admin-users-delete').removeClass('is-active')
},
deleteUser: (ev) => {
vueDeleteUser.loading = true
$.ajax('/admin/users/' + usrData._id, {
dataType: 'json',
method: 'DELETE'
}).then((rData, rStatus, rXHR) => {
vueDeleteUser.loading = false
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)
}
/* global $, _, currentBasePath */
'use strict'
import $ from 'jquery'
import _ from 'lodash'
import { setInputSelection } from '../helpers/form'
import { makeSafePath } from '../helpers/pages'
// -> Create New Document
let suggestedCreatePath = currentBasePath + '/new-page'
module.exports = (currentBasePath) => {
let suggestedCreatePath = currentBasePath + '/new-page'
$('.btn-create-prompt').on('click', (ev) => {
$('#txt-create-prompt').val(suggestedCreatePath)
$('#modal-create-prompt').toggleClass('is-active')
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length) // eslint-disable-line no-undef
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
$('.btn-create-prompt').on('click', (ev) => {
$('#txt-create-prompt').val(suggestedCreatePath)
$('#modal-create-prompt').toggleClass('is-active')
setInputSelection($('#txt-create-prompt').get(0), currentBasePath.length + 1, suggestedCreatePath.length)
$('#txt-create-prompt').removeClass('is-danger').next().addClass('is-hidden')
})
$('#txt-create-prompt').on('keypress', (ev) => {
if (ev.which === 13) {
$('.btn-create-go').trigger('click')
}
})
$('#txt-create-prompt').on('keypress', (ev) => {
if (ev.which === 13) {
$('.btn-create-go').trigger('click')
}
})
$('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val()) // eslint-disable-line no-undef
if (_.isEmpty(newDocPath)) {
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-create-prompt').parent().addClass('is-loading')
window.location.assign('/create/' + newDocPath)
}
})
$('.btn-create-go').on('click', (ev) => {
let newDocPath = makeSafePath($('#txt-create-prompt').val())
if (_.isEmpty(newDocPath)) {
$('#txt-create-prompt').addClass('is-danger').next().removeClass('is-hidden')
} else {
$('#txt-create-prompt').parent().addClass('is-loading')
window.location.assign('/create/' + newDocPath)
}
})
}
/* global $, _, alerts, currentBasePath */
'use strict'
import $ from 'jquery'
import _ from 'lodash'
import { makeSafePath } from '../helpers/form'
import { setInputSelection } from '../helpers/pages'
// -> Move Existing Document
if (currentBasePath !== '') {
$('.btn-move-prompt').removeClass('is-hidden')
}
module.exports = (currentBasePath, alerts) => {
if (currentBasePath !== '') {
$('.btn-move-prompt').removeClass('is-hidden')
}
let moveInitialDocument = _.lastIndexOf(currentBasePath, '/') + 1
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')
})
$('.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()) // 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.')
})
}
})
$('#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.')
})
}
})
}
/* global $, Vue, alerts, _, usrData, usrDataName */
'use strict'
if ($('#page-type-admin-profile').length) {
let vueProfile = new Vue({
el: '#page-type-admin-profile',
data: {
password: '********',
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)
})
}
},
created: function () {
this.name = usrDataName
}
})
} else if ($('#page-type-admin-users').length) {
/* global usrData, usrDataName */
/* eslint-disable spaced-comment */
//=include ../modals/admin-users-create.js
/* eslint-enable spaced-comment */
import $ from 'jquery'
import _ from 'lodash'
import Vue from 'vue'
} else if ($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({
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) => {
_.pullAt(vueEditUser.rights, idx)
vueEditUser.$forceUpdate()
module.exports = (alerts) => {
if ($('#page-type-admin-profile').length) {
let vueProfile = new Vue({
el: '#page-type-admin-profile',
data: {
password: '********',
passwordVerify: '********',
name: ''
},
saveUser: (ev) => {
let formattedRights = _.cloneDeep(vueEditUser.rights)
switch (vueEditUser.roleoverride) {
case 'admin':
formattedRights.push({
role: 'admin',
path: '/',
exact: false,
deny: false
})
break
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: 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)
})
}
},
created: function () {
this.id = usrData._id
this.email = usrData.email
this.name = usrData.name
if (_.find(usrData.rights, { role: 'admin' })) {
this.rights = _.reject(usrData.rights, ['role', 'admin'])
this.roleoverride = 'admin'
} else {
this.rights = usrData.rights
}
}
})
/* eslint-disable spaced-comment */
//=include ../modals/admin-users-delete.js
/* eslint-enable spaced-comment */
} else if ($('#page-type-admin-settings').length) {
let vueSettings = new Vue({ // eslint-disable-line no-unused-vars
el: '#page-type-admin-settings',
data: {
upgradeModal: {
state: false,
step: 'confirm',
mode: 'upgrade',
error: 'Something went wrong.'
}
},
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
})
created: function () {
this.name = usrDataName
}
})
} else if ($('#page-type-admin-users').length) {
require('../modals/admin-users-create.js')(alerts)
} else if ($('#page-type-admin-users-edit').length) {
let vueEditUser = new Vue({
el: '#page-type-admin-users-edit',
data: {
id: '',
email: '',
password: '********',
name: '',
rights: [],
roleoverride: 'none'
},
flushcache: (ev) => {
window.alert('Coming soon!')
methods: {
addRightsRow: (ev) => {
vueEditUser.rights.push({
role: 'write',
path: '/',
exact: false,
deny: false
})
},
removeRightsRow: (idx) => {
_.pullAt(vueEditUser.rights, idx)
vueEditUser.$forceUpdate()
},
saveUser: (ev) => {
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)
})
}
},
resetaccounts: (ev) => {
window.alert('Coming soon!')
created: function () {
this.id = usrData._id
this.email = usrData.email
this.name = usrData.name
if (_.find(usrData.rights, { role: 'admin' })) {
this.rights = _.reject(usrData.rights, ['role', 'admin'])
this.roleoverride = 'admin'
} else {
this.rights = usrData.rights
}
}
})
require('../modals/admin-users-delete.js')(alerts)
} else if ($('#page-type-admin-settings').length) {
let vueSettings = new Vue({ // eslint-disable-line no-unused-vars
el: '#page-type-admin-settings',
data: {
upgradeModal: {
state: false,
step: 'confirm',
mode: 'upgrade',
error: 'Something went wrong.'
}
},
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!')
}
}
}
})
})
}
}
/* global $ */
'use strict'
if ($('#page-type-edit').length) {
let pageEntryPath = $('#page-type-edit').data('entrypath') // eslint-disable-line no-unused-vars
// let pageCleanExit = false
import $ from 'jquery'
// -> 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) => {
$('#modal-edit-discard').toggleClass('is-active')
})
// -> Discard
// window.onbeforeunload = function () {
// return (pageCleanExit) ? true : 'Unsaved modifications will be lost. Are you sure you want to navigate away from this page?'
// }
$('.btn-edit-discard').on('click', (ev) => {
$('#modal-edit-discard').toggleClass('is-active')
})
/* eslint-disable spaced-comment */
//=include ../components/editor.js
/* eslint-enable spaced-comment */
// window.onbeforeunload = function () {
// return (pageCleanExit) ? true : 'Unsaved modifications will be lost. Are you sure you want to navigate away from this page?'
// }
require('../components/editor.js')(alerts, pageEntryPath, socket)
}
}
/* global $, ace */
'use strict'
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()
import $ from 'jquery'
import * as ace from 'brace'
import 'brace/theme/tomorrow_night'
import 'brace/mode/markdown'
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 */
//=include ../modals/create.js
//=include ../modals/move.js
/* eslint-enable spaced-comment */
let currentBasePath = ($('#page-type-source').data('entrypath') !== 'home') ? $('#page-type-source').data('entrypath') : ''
require('../modals/create.js')(currentBasePath)
require('../modals/move.js')(currentBasePath, alerts)
}
}
/* global $ */
'use strict'
if ($('#page-type-view').length) {
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : '' // eslint-disable-line no-unused-vars
import $ from 'jquery'
/* eslint-disable spaced-comment */
//=include ../modals/create.js
//=include ../modals/move.js
/* eslint-enable spaced-comment */
module.exports = (alerts) => {
if ($('#page-type-view').length) {
let currentBasePath = ($('#page-type-view').data('entrypath') !== 'home') ? $('#page-type-view').data('entrypath') : ''
require('../modals/create.js')(currentBasePath)
require('../modals/move.js')(currentBasePath, alerts)
}
}
......@@ -19,6 +19,8 @@ $primary: 'indigo';
@import './libs/twemoji-awesome';
@import './libs/jquery-contextmenu';
@import 'node_modules/highlight.js/styles/tomorrow';
@import 'node_modules/simplemde/dist/simplemde.min';
@import './components/_editor';
......
......@@ -27,6 +27,21 @@ const args = require('yargs')
.alias('h', 'help')
.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) {
// =============================================
// DEVELOPER MODE
......@@ -41,9 +56,8 @@ if (args.d) {
const fuse = fsbx.FuseBox.init({
homeDir: './client',
outFile: './assets/js/bundle.min.js',
alias: {
vue: 'vue/dist/vue.js'
},
alias: ALIASES,
shim: SHIMS,
plugins: [
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
......@@ -55,7 +69,8 @@ if (args.d) {
fuse.devServer('>index.js', {
port: 4444,
httpServer: false
httpServer: false,
hmr: false
})
// Server
......@@ -80,7 +95,7 @@ if (args.d) {
}, 1000)
} else if (args.c) {
// =============================================
// DEVELOPER MODE
// 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({
homeDir: './client',
outFile: './assets/js/configure.min.js',
alias: {
vue: 'vue/dist/vue.js'
},
alias: ALIASES,
shim: SHIMS,
plugins: [
[ fsbx.SassPlugin({ includePaths: ['../core'] }), fsbx.CSSPlugin() ],
fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }),
......@@ -131,9 +145,8 @@ if (args.d) {
const fuse = fsbx.FuseBox.init({
homeDir: './client',
alias: {
vue: 'vue/dist/vue.js'
},
alias: ALIASES,
shim: SHIMS,
plugins: [
[ fsbx.SassPlugin({ outputStyle: 'compressed', includePaths: ['./node_modules/requarks-core'] }), fsbx.CSSPlugin() ],
fsbx.BabelPlugin({
......
......@@ -43,13 +43,13 @@
},
"dependencies": {
"auto-load": "^2.1.0",
"axios": "^0.15.3",
"axios": "^0.16.0",
"bcryptjs-then": "^1.0.1",
"bluebird": "^3.4.7",
"body-parser": "^1.17.1",
"bunyan": "^1.8.9",
"cheerio": "^0.22.0",
"child-process-promise": "^2.2.0",
"child-process-promise": "^2.2.1",
"chokidar": "^1.6.0",
"commander": "^2.9.0",
"compression": "^1.6.2",
......@@ -61,7 +61,7 @@
"express": "^4.15.2",
"express-brute": "^1.0.0",
"express-brute-mongoose": "0.0.7",
"express-session": "^1.15.1",
"express-session": "^1.15.2",
"file-type": "^4.0.0",
"filesize.js": "^1.0.2",
"follow-redirects": "^1.2.3",
......@@ -85,19 +85,19 @@
"markdown-it-expand-tabs": "^1.0.11",
"markdown-it-external-links": "0.0.6",
"markdown-it-footnote": "^3.0.1",
"markdown-it-task-lists": "^1.4.1",
"markdown-it-task-lists": "^2.0.0",
"memdown": "^1.2.4",
"mime-types": "^2.1.15",
"moment": "^2.18.1",
"moment-timezone": "^0.5.11",
"mongodb": "^2.2.25",
"mongoose": "^4.9.1",
"mongoose": "^4.9.2",
"multer": "^1.2.1",
"ora": "^1.2.0",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"passport.socketio": "^3.7.0",
"pm2": "^2.4.2",
"pm2": "^2.4.3",
"pug": "^2.0.0-beta11",
"read-chunk": "^2.0.0",
"remove-markdown": "^0.1.0",
......@@ -119,33 +119,33 @@
"winston": "^2.3.0"
},
"devDependencies": {
"ace-builds": "^1.2.6",
"babel-cli": "^6.24.0",
"babel-jest": "^19.0.0",
"babel-preset-es2015": "^6.24.0",
"brace": "^0.10.0",
"colors": "^1.1.2",
"eslint": "^3.18.0",
"eslint": "^3.19.0",
"eslint-config-standard": "^7.1.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-standard": "^2.1.1",
"fuse-box": "^1.3.128",
"fuse-box": "^1.3.129",
"jest": "^19.0.2",
"jquery": "^3.2.1",
"jquery-contextmenu": "^2.4.4",
"jquery-simple-upload": "^1.0.0",
"jquery-smooth-scroll": "^2.0.0",
"node-sass": "^4.5.1",
"node-sass": "^4.5.2",
"nodemon": "^1.11.0",
"pre-commit": "^1.2.2",
"pug-lint": "^2.4.0",
"snyk": "^1.25.1",
"snyk": "^1.26.1",
"standard": "^9.0.2",
"sticky-js": "^1.1.9",
"twemoji-awesome": "^1.0.4",
"vee-validate": "^2.0.0-beta.25",
"vue": "^2.2.5"
"vue": "^2.2.6"
},
"standard": {
"globals": [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment