Insert Image + alignment

This commit is contained in:
NGPixel 2016-09-25 17:32:39 -04:00
parent c0be18a8d8
commit 86524e83bb
12 changed files with 219 additions and 38 deletions

View File

@ -53,6 +53,7 @@ var path = require('path');
var cron = require('cron').CronJob; var cron = require('cron').CronJob;
var readChunk = require('read-chunk'); var readChunk = require('read-chunk');
var fileType = require('file-type'); var fileType = require('file-type');
var farmhash = require('farmhash');
global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 }); global.ws = require('socket.io-client')('http://localhost:' + appconfig.wsPort, { reconnectionAttempts: 10 });
@ -177,6 +178,7 @@ var job = new cron({
return Promise.map(fList, (f) => { return Promise.map(fList, (f) => {
let fPath = path.join(fldPath, f); let fPath = path.join(fldPath, f);
let fPathObj = path.parse(fPath); let fPathObj = path.parse(fPath);
let fUid = farmhash.fingerprint32(fldName + '/' + f);
return fs.statAsync(fPath) return fs.statAsync(fPath)
.then((s) => { .then((s) => {
@ -193,10 +195,11 @@ var job = new cron({
if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) { if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
return lcdata.getImageMetadata(fPath).then((mData) => { return lcdata.getImageMetadata(fPath).then((mData) => {
let cacheThumbnailPath = path.parse(path.join(dataPath, 'thumbs', fldName, fPathObj.name + '.png')); let cacheThumbnailPath = path.parse(path.join(dataPath, 'thumbs', fUid + '.png'));
let cacheThumbnailPathStr = path.format(cacheThumbnailPath); let cacheThumbnailPathStr = path.format(cacheThumbnailPath);
mData = _.pick(mData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']); mData = _.pick(mData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']);
mData.uid = fUid;
mData.category = 'image'; mData.category = 'image';
mData.mime = mimeInfo.mime; mData.mime = mimeInfo.mime;
mData.folder = fldName; mData.folder = fldName;
@ -227,6 +230,7 @@ var job = new cron({
// Other Files // Other Files
allFiles.push({ allFiles.push({
uid: fUid,
category: 'file', category: 'file',
mime: mimeInfo.mime, mime: mimeInfo.mime,
folder: fldName, folder: fldName,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -6,8 +6,12 @@ let vueImage = new Vue({
isLoadingText: '', isLoadingText: '',
newFolderName: '', newFolderName: '',
newFolderShow: false, newFolderShow: false,
fetchFromUrlURL: '',
fetchFromUrlShow: false,
folders: [], folders: [],
currentFolder: '', currentFolder: '',
currentImage: '',
currentAlign: 'left',
images: [] images: []
}, },
methods: { methods: {
@ -20,12 +24,45 @@ let vueImage = new Vue({
mdeModalOpenState = false; mdeModalOpenState = false;
$('#modal-editor-image').slideUp(); $('#modal-editor-image').slideUp();
}, },
insertImage: (ev) => {
if(mde.codemirror.doc.somethingSelected()) {
mde.codemirror.execCommand('singleSelection');
}
let selImage = _.find(vueImage.images, ['uid', vueImage.currentImage]);
selImage.normalizedPath = (selImage.folder === '') ? selImage.filename : selImage.folder + '/' + 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();
},
newFolder: (ev) => { newFolder: (ev) => {
vueImage.newFolderShow = true; vueImage.newFolderShow = true;
}, },
newFolderDiscard: (ev) => { newFolderDiscard: (ev) => {
vueImage.newFolderShow = false; vueImage.newFolderShow = false;
}, },
fetchFromUrl: (ev) => {
vueImage.fetchFromUrlShow = true;
},
fetchFromUrlDiscard: (ev) => {
vueImage.fetchFromUrlShow = false;
},
selectFolder: (fldName) => { selectFolder: (fldName) => {
vueImage.currentFolder = fldName; vueImage.currentFolder = fldName;
vueImage.loadImages(); vueImage.loadImages();
@ -34,6 +71,7 @@ let vueImage = new Vue({
vueImage.isLoading = true; vueImage.isLoading = true;
vueImage.isLoadingText = 'Fetching folders list...'; vueImage.isLoadingText = 'Fetching folders list...';
vueImage.currentFolder = ''; vueImage.currentFolder = '';
vueImage.currentImage = '';
Vue.nextTick(() => { Vue.nextTick(() => {
socket.emit('uploadsGetFolders', { }, (data) => { socket.emit('uploadsGetFolders', { }, (data) => {
vueImage.folders = data; vueImage.folders = data;
@ -46,13 +84,16 @@ let vueImage = new Vue({
vueImage.isLoadingText = 'Fetching images...'; vueImage.isLoadingText = 'Fetching images...';
Vue.nextTick(() => { Vue.nextTick(() => {
socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => { socket.emit('uploadsGetImages', { folder: vueImage.currentFolder }, (data) => {
vueImage.images = _.map(data, (f) => { vueImage.images = data;
f.thumbpath = (f.folder === '') ? f.basename + '.png' : _.join([ f.folder, f.basename + '.png' ], '/');
return f;
});
vueImage.isLoading = false; vueImage.isLoading = false;
}); });
}); });
},
selectImage: (imageId) => {
vueImage.currentImage = imageId;
},
selectAlignment: (align) => {
vueImage.currentAlign = align;
} }
} }
}); });

View File

@ -8,6 +8,10 @@ if($('#mk-editor').length === 1) {
let mdeModalOpenState = false; let mdeModalOpenState = false;
let mdeCurrentEditor = null; let mdeCurrentEditor = null;
Vue.filter('filesize', (v) => {
return _.toUpper(filesize(v));
})
//=include editor-image.js //=include editor-image.js
//=include editor-codeblock.js //=include editor-codeblock.js

View File

@ -65,6 +65,99 @@
} }
.editor-modal-imagechoices {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
max-height: 450px;
overflow: auto;
overflow-x: hidden;
> figure {
display: flex;
flex-direction: column;
background-color: #FAFAFA;
border-radius: 5px;
padding: 5px;
width: 160px;
min-height: 205px;
margin: 0 5px 10px 5px;
cursor: pointer;
justify-content: center;
align-items: center;
transition: background-color 0.4s ease;
> img {
border: 1px solid #DDD;
border-radius: 5px;
padding: 2px;
background-color: #FFF;
margin: 0 0 5px 0;
}
> span {
font-size: 12px;
> strong {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
display: block;
width: 150px;
text-align: center;
}
}
&:hover {
background-color: #DDD;
}
&.is-active {
background-color: $primary;
color: #FFF;
> img {
border-color: darken($primary, 10%);
}
> span > strong {
color: #FFF;
}
}
}
}
.editor-modal-imagealign {
.control > span {
letter-spacing: 1px;
text-transform: uppercase;
color: #aeb1b5;
font-size: 11px;
}
> .is-grouped {
display: flex;
align-items: center;
justify-content: center;
}
.button > .icon {
margin: 0;
}
}
.editor-modal-folderlist {
height: 358px;
overflow: auto;
overflow-x: hidden;
}
.CodeMirror { .CodeMirror {
border-left: none; border-left: none;
border-right: none; border-right: none;

View File

@ -5,7 +5,7 @@ var router = express.Router();
var _ = require('lodash'); var _ = require('lodash');
var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$"); var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$");
var validPathThumbsRe = new RegExp("^([a-z0-9\\/-]+\\.png)$"); var validPathThumbsRe = new RegExp("^([0-9]+\\.png)$");
// ========================================== // ==========================================
// SERVE UPLOADS FILES // SERVE UPLOADS FILES

View File

@ -23,12 +23,15 @@ var paths = {
'./node_modules/jquery/dist/jquery.min.js', './node_modules/jquery/dist/jquery.min.js',
'./node_modules/vue/dist/vue.min.js', './node_modules/vue/dist/vue.min.js',
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js', './node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
'./node_modules/jquery-contextmenu/dist/jquery.ui.position.min.js',
'./node_modules/jquery-contextmenu/dist/jquery.contextMenu.min.js',
'./node_modules/sticky-js/dist/sticky.min.js', './node_modules/sticky-js/dist/sticky.min.js',
'./node_modules/simplemde/dist/simplemde.min.js', './node_modules/simplemde/dist/simplemde.min.js',
'./node_modules/ace-builds/src-min-noconflict/ace.js', './node_modules/ace-builds/src-min-noconflict/ace.js',
'./node_modules/ace-builds/src-min-noconflict/ext-modelist.js', './node_modules/ace-builds/src-min-noconflict/ext-modelist.js',
'./node_modules/ace-builds/src-min-noconflict/mode-markdown.js', './node_modules/ace-builds/src-min-noconflict/mode-markdown.js',
'./node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js', './node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js',
'./node_modules/filesize.js/dist/filesize.min.js',
'./node_modules/lodash/lodash.min.js' './node_modules/lodash/lodash.min.js'
], ],
scriptlibs_acemodes: [ scriptlibs_acemodes: [
@ -97,7 +100,7 @@ gulp.task("scripts-libs", function () {
return merge( return merge(
gulp.src(paths.scriptlibs) gulp.src(paths.scriptlibs)
.pipe(concat('libs.js')) .pipe(concat('libs.js', {newLine: ';\n'}))
.pipe(uglify({ mangle: false })) .pipe(uglify({ mangle: false }))
.pipe(gulp.dest("./assets/js")), .pipe(gulp.dest("./assets/js")),

View File

@ -201,9 +201,9 @@ module.exports = {
*/ */
getUploadsFiles(cat, fld) { getUploadsFiles(cat, fld) {
return this._uploadsDb.Files.find({ return this._uploadsDb.Files.chain().find({
'$and': [{ 'category' : cat },{ 'folder' : fld }] '$and': [{ 'category' : cat },{ 'folder' : fld }]
}); }).simplesort('filename').data();
}, },

View File

@ -91,10 +91,11 @@
"devDependencies": { "devDependencies": {
"ace-builds": "^1.2.5", "ace-builds": "^1.2.5",
"babel-preset-es2015": "^6.14.0", "babel-preset-es2015": "^6.14.0",
"bulma": "^0.2.0", "bulma": "^0.1.2",
"chai": "^3.5.0", "chai": "^3.5.0",
"chai-as-promised": "^5.3.0", "chai-as-promised": "^5.3.0",
"codacy-coverage": "^2.0.0", "codacy-coverage": "^2.0.0",
"filesize.js": "^1.0.1",
"font-awesome": "^4.6.3", "font-awesome": "^4.6.3",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-babel": "^6.1.2", "gulp-babel": "^6.1.2",
@ -110,6 +111,7 @@
"gulp-zip": "^3.2.0", "gulp-zip": "^3.2.0",
"istanbul": "^0.4.5", "istanbul": "^0.4.5",
"jquery": "^3.1.1", "jquery": "^3.1.1",
"jquery-contextmenu": "^2.2.4",
"jquery-smooth-scroll": "^2.0.0", "jquery-smooth-scroll": "^2.0.0",
"merge-stream": "^1.0.0", "merge-stream": "^1.0.0",
"mocha": "^3.0.2", "mocha": "^3.0.2",

View File

@ -32,7 +32,7 @@
.columns .columns
.column.is-one-quarter(style={'max-width':'350px'}) .column.is-one-quarter(style={'max-width':'350px'})
.box(style={'max-height': '400px', overflow: 'auto', 'overflow-x': 'hidden'}) .box.editor-modal-folderlist
aside.menu aside.menu
p.menu-label p.menu-label
| Folders | Folders
@ -41,9 +41,25 @@
a(v-on:click="selectFolder(fld)", v-bind:class="{ 'is-active': currentFolder === fld }") a(v-on:click="selectFolder(fld)", v-bind:class="{ 'is-active': currentFolder === fld }")
span.icon.is-small: i.fa.fa-folder span.icon.is-small: i.fa.fa-folder
span /{{ fld }} span /{{ fld }}
.column .box.editor-modal-imagealign
figure.image.is-128x128(v-for="img in images") .control.is-grouped
img(v-bind:src="'/uploads/t/' + img.thumbpath") .control
span Alignment
.control.has-addons
a.button.is-primary(title="Left", v-on:click="selectAlignment('left')", v-bind:class="{ 'is-outlined': currentAlign !== 'left' }")
span.icon.is-small: i.fa.fa-align-left
a.button.is-primary(title="Center", v-on:click="selectAlignment('center')", v-bind:class="{ 'is-outlined': currentAlign !== 'center' }")
span.icon.is-small: i.fa.fa-align-center
a.button.is-primary(title="Right", v-on:click="selectAlignment('right')", v-bind:class="{ 'is-outlined': currentAlign !== 'right' }")
span.icon.is-small: i.fa.fa-align-right
.control
a.button.is-primary(title="Page Logo", v-on:click="selectAlignment('logo')", v-bind:class="{ 'is-outlined': currentAlign !== 'logo' }")
span.icon.is-small: i.fa.fa-external-link-square
.column.editor-modal-imagechoices
figure(v-for="img in images", v-bind:class="{ 'is-active': currentImage === img.uid }", v-on:click="selectImage(img.uid)")
img(v-bind:src="'/uploads/t/' + img.uid + '.png'")
span: strong {{ img.basename }}
span {{ img.filesize | filesize }}
.modal(v-bind:class="{ 'is-active': newFolderShow }") .modal(v-bind:class="{ 'is-active': newFolderShow }")
.modal-background .modal-background
@ -61,3 +77,20 @@
footer.card-footer footer.card-footer
a.card-footer-item(v-on:click="newFolderDiscard") Discard a.card-footer-item(v-on:click="newFolderDiscard") Discard
a.card-footer-item(v-on:click="newFolderCreate") Create a.card-footer-item(v-on:click="newFolderCreate") Create
.modal(v-bind:class="{ 'is-active': fetchFromUrlShow }")
.modal-background
.modal-container
.modal-content
.card.is-fullwidth
header.card-header
p.card-header-title Fetch Image from URL
.card-content
.content
label.label Enter full URL path to the image:
p.control
input.input(type='text', placeholder='http://www.example.com/some-image.png', v-model='fetchFromUrlURL')
span.help.is-danger.is-hidden This URL path is invalid!
footer.card-footer
a.card-footer-item(v-on:click="fetchFromUrlDiscard") Discard
a.card-footer-item(v-on:click="fetchFromUrlFetch") Fetch