Browse Source

Insert Image + alignment

pull/2/head
NGPixel 8 years ago
parent
commit
86524e83bb
12 changed files with 219 additions and 38 deletions
  1. 6
      agent.js
  2. 2
      assets/css/app.css
  3. 2
      assets/js/app.js
  4. 43
      assets/js/libs.js
  5. 49
      client/js/components/editor-image.js
  6. 4
      client/js/components/editor.js
  7. 93
      client/scss/components/_editor.scss
  8. 2
      controllers/uploads.js
  9. 5
      gulpfile.js
  10. 4
      models/localdata.js
  11. 4
      package.json
  12. 43
      views/modals/editor-image.pug

6
agent.js

@ -53,6 +53,7 @@ var path = require('path');
var cron = require('cron').CronJob;
var readChunk = require('read-chunk');
var fileType = require('file-type');
var farmhash = require('farmhash');
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) => {
let fPath = path.join(fldPath, f);
let fPathObj = path.parse(fPath);
let fUid = farmhash.fingerprint32(fldName + '/' + f);
return fs.statAsync(fPath)
.then((s) => {
@ -193,10 +195,11 @@ var job = new cron({
if(_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
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);
mData = _.pick(mData, ['format', 'width', 'height', 'density', 'hasAlpha', 'orientation']);
mData.uid = fUid;
mData.category = 'image';
mData.mime = mimeInfo.mime;
mData.folder = fldName;
@ -227,6 +230,7 @@ var job = new cron({
// Other Files
allFiles.push({
uid: fUid,
category: 'file',
mime: mimeInfo.mime,
folder: fldName,

2
assets/css/app.css
File diff suppressed because it is too large
View File

2
assets/js/app.js
File diff suppressed because it is too large
View File

43
assets/js/libs.js
File diff suppressed because it is too large
View File

49
client/js/components/editor-image.js

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

4
client/js/components/editor.js

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

93
client/scss/components/_editor.scss

@ -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 {
border-left: none;
border-right: none;

2
controllers/uploads.js

@ -5,7 +5,7 @@ var router = express.Router();
var _ = require('lodash');
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

5
gulpfile.js

@ -23,12 +23,15 @@ var paths = {
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/vue/dist/vue.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/simplemde/dist/simplemde.min.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/mode-markdown.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'
],
scriptlibs_acemodes: [
@ -97,7 +100,7 @@ gulp.task("scripts-libs", function () {
return merge(
gulp.src(paths.scriptlibs)
.pipe(concat('libs.js'))
.pipe(concat('libs.js', {newLine: ';\n'}))
.pipe(uglify({ mangle: false }))
.pipe(gulp.dest("./assets/js")),

4
models/localdata.js

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

4
package.json

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

43
views/modals/editor-image.pug

@ -32,7 +32,7 @@
.columns
.column.is-one-quarter(style={'max-width':'350px'})
.box(style={'max-height': '400px', overflow: 'auto', 'overflow-x': 'hidden'})
.box.editor-modal-folderlist
aside.menu
p.menu-label
| Folders
@ -41,9 +41,25 @@
a(v-on:click="selectFolder(fld)", v-bind:class="{ 'is-active': currentFolder === fld }")
span.icon.is-small: i.fa.fa-folder
span /{{ fld }}
.column
figure.image.is-128x128(v-for="img in images")
img(v-bind:src="'/uploads/t/' + img.thumbpath")
.box.editor-modal-imagealign
.control.is-grouped
.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-background
@ -60,4 +76,21 @@
span.help.is-danger.is-hidden This folder name is invalid!
footer.card-footer
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
Loading…
Cancel
Save