Browse Source

Insert Image + alignment

NGPixel 8 years ago
parent
commit
86524e83bb

+ 5 - 1
agent.js

@@ -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 it is too large
+ 0 - 0
assets/css/app.css


File diff suppressed because it is too large
+ 0 - 0
assets/js/app.js


File diff suppressed because it is too large
+ 0 - 0
assets/js/libs.js


+ 45 - 4
client/js/components/editor-image.js

@@ -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) => {
-						f.thumbpath = (f.folder === '') ? f.basename + '.png' : _.join([ f.folder, f.basename + '.png' ], '/');
-						return f;
-					});
+					vueImage.images = data;
 					vueImage.isLoading = false;
 					vueImage.isLoading = false;
 				});
 				});
 			});
 			});
+		},
+		selectImage: (imageId) => {
+			vueImage.currentImage = imageId;
+		},
+		selectAlignment: (align) => {
+			vueImage.currentAlign = align;
 		}
 		}
 	}
 	}
 });
 });

+ 4 - 0
client/js/components/editor.js

@@ -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
 
 

+ 93 - 0
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 {
 .CodeMirror {
 	border-left: none;
 	border-left: none;
 	border-right: none;
 	border-right: none;

+ 1 - 1
controllers/uploads.js

@@ -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

+ 4 - 1
gulpfile.js

@@ -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")),
 
 

+ 2 - 2
models/localdata.js

@@ -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();
 
 
 	},
 	},
 
 

+ 3 - 1
package.json

@@ -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",

+ 38 - 5
views/modals/editor-image.pug

@@ -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
-				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(v-bind:class="{ 'is-active': newFolderShow }")
 		.modal-background
 		.modal-background
@@ -60,4 +76,21 @@
 								span.help.is-danger.is-hidden This folder name is invalid!
 								span.help.is-danger.is-hidden This folder name is invalid!
 					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

Some files were not shown because too many files changed in this diff