Pārlūkot izejas kodu

Video player modal + provider match

NGPixel 8 gadi atpakaļ
vecāks
revīzija
4625a302f6

+ 1 - 0
README.md

@@ -49,6 +49,7 @@
 	- [ ] Links
 	- [x] Image Selection modal
 	- [x] File Selection modal
+	- [x] Video player
 	- [x] Inline Code
 	- [x] Code Editor modal
 	- [ ] Table Editor

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
assets/css/app.css


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
assets/js/app.js


+ 3 - 3
client/js/components/alerts.js

@@ -42,7 +42,7 @@ class Alerts {
 
 		let nAlert = _.defaults(options, {
 			_uid: self.uidNext,
-			class: 'is-info',
+			class: 'info',
 			message: '---',
 			sticky: false,
 			title: '---'
@@ -68,7 +68,7 @@ class Alerts {
 	 */
 	pushError(title, message) {
 		this.push({
-			class: 'is-danger',
+			class: 'error',
 			message,
 			sticky: false,
 			title
@@ -83,7 +83,7 @@ class Alerts {
 	 */
 	pushSuccess(title, message) {
 		this.push({
-			class: 'is-success',
+			class: 'success',
 			message,
 			sticky: false,
 			title

+ 49 - 0
client/js/components/editor-video.js

@@ -0,0 +1,49 @@
+
+const videoRules = {
+	'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
+	'vimeo': new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
+	'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
+};
+
+// Vue Video instance
+
+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;
+			$('#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();
+
+		}
+	}
+});

+ 23 - 22
client/js/components/editor.js

@@ -14,6 +14,7 @@ if($('#mk-editor').length === 1) {
 
 	//=include editor-image.js
 	//=include editor-file.js
+	//=include editor-video.js
 	//=include editor-codeblock.js
 
 	var mde = new SimpleMDE({
@@ -70,7 +71,7 @@ if($('#mk-editor').length === 1) {
 			{
 				name: "unordered-list",
 				action: SimpleMDE.toggleUnorderedList,
-				className: "icon-list-ul",
+				className: "icon-th-list",
 				title: "Bullet List",
 			},
 			{
@@ -98,7 +99,7 @@ if($('#mk-editor').length === 1) {
 						vueImage.open();
 					}
 				},
-				className: "icon-image3",
+				className: "icon-image",
 				title: "Insert Image",
 			},
 			{
@@ -108,15 +109,15 @@ if($('#mk-editor').length === 1) {
 						vueFile.open();
 					}
 				},
-				className: "icon-file-text-o",
+				className: "icon-paper",
 				title: "Insert File",
 			},
 			{
 				name: "video",
 				action: (editor) => {
-					/*if(!mdeModalOpenState) {
-						vueFile.open();
-					}*/
+					if(!mdeModalOpenState) {
+						vueVideo.open();
+					}
 				},
 				className: "icon-video-camera2",
 				title: "Insert Video Player",
@@ -180,21 +181,6 @@ if($('#mk-editor').length === 1) {
 
 	//-> Save
 
-	$('.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;
-			}
-		}
-	});
-
 	let saveCurrentDocument = (ev) => {
 		$.ajax(window.location.href, {
 			data: {
@@ -211,6 +197,21 @@ if($('#mk-editor').length === 1) {
 		}, (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;
+			}
+		}
+	});
 
 }

+ 4 - 4
controllers/pages.js

@@ -43,9 +43,9 @@ router.put('/edit/*', (req, res, next) => {
 	let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
 
 	entries.update(safePath, req.body.markdown).then(() => {
-		res.json({
+		return res.json({
 			ok: true
-		});
+		}) || true;
 	}).catch((err) => {
 		res.json({
 			ok: false,
@@ -105,9 +105,9 @@ router.put('/create/*', (req, res, next) => {
 	let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
 
 	entries.create(safePath, req.body.markdown).then(() => {
-		res.json({
+		return res.json({
 			ok: true
-		});
+		}) || true;
 	}).catch((err) => {
 		res.json({
 			ok: false,

+ 1 - 1
gulpfile.js

@@ -196,7 +196,7 @@ gulp.task('dev', function() {
 
 	return run('default');
 
-})
+});
 
 /**
  * TASK - Creates deployment packages

+ 3 - 2
libs/local.js

@@ -158,8 +158,9 @@ module.exports = {
 	/**
 	 * Check if filename is valid and unique
 	 *
-	 * @param      {String}           f       The filename
-	 * @param      {String}           fld     The containing folder
+	 * @param      {String}           f        The filename
+	 * @param      {String}           fld      The containing folder
+	 * @param      {boolean}          isImage  Indicates if image
 	 * @return     {Promise<String>}  Promise of the accepted filename
 	 */
 	validateUploadsFilename(f, fld, isImage) {

+ 44 - 0
libs/markdown.js

@@ -58,6 +58,31 @@ mkdown.renderer.rules.emoji = function(token, idx) {
 	return '<i class="twa twa-' + _.replace(token[idx].markup, /_/g, '-') + '"></i>';
 };
 
+// Video rules
+
+const videoRules = [
+	{
+		selector: 'a.youtube',
+		regexp: new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
+		output: '<iframe width="640" height="360" src="https://www.youtube.com/embed/{0}?rel=0" frameborder="0" allowfullscreen></iframe>'
+	},
+	{
+		selector: 'a.vimeo',
+		regexp: new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
+		output: '<iframe src="https://player.vimeo.com/video/{0}" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
+	},
+	{
+		selector: 'a.dailymotion',
+		regexp: new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i'),
+		output: '<iframe width="640" height="360" src="//www.dailymotion.com/embed/video/{0}?endscreen-enable=false" frameborder="0" allowfullscreen></iframe>'
+	},
+	{
+		selector: 'a.video',
+		regexp: false,
+		output: '<video width="640" height="360" controls preload="metadata"><source src="{0}" type="video/mp4"></video>'
+	}
+]
+
 /**
  * Parse markdown content and build TOC tree
  *
@@ -204,6 +229,25 @@ const parseContent = (content)  => {
 		});
 	});
 
+	// Replace video links with embeds
+
+	_.forEach(videoRules, (vrule) => {
+		cr(vrule.selector).each((i, elm) => {
+			let originLink = cr(elm).attr('href');
+			if(vrule.regexp) {
+				let vidMatches = originLink.match(vrule.regexp);
+				if((vidMatches && _.isArray(vidMatches))) {
+					vidMatches = _.filter(vidMatches, (f) => {
+						return f && _.isString(f);
+					});
+					originLink = _.last(vidMatches);
+				}
+			}
+			let processedLink = _.replace(vrule.output, '{0}', originLink);
+			cr(elm).replaceWith(processedLink);
+		});
+	});
+
 	output = cr.html();
 
 	return output;

+ 1 - 1
libs/uploads.js

@@ -222,7 +222,7 @@ module.exports = {
 
 					destFileStream.on('finish', () => {
 						resolve(true);
-					})
+					});
 
 					rq.pipe(destFileStream);
 

+ 5 - 7
views/common/alerts.pug

@@ -1,16 +1,14 @@
 #alerts
 	ul
 		template(v-for="aItem in children", track-by='_uid')
-			.notification(v-bind:class='aItem.class')
+			li(v-bind:class='aItem.class')
 				button.delete(v-on:click='acknowledge(aItem._uid)')
 				h3 {{ aItem.title }}
 				span {{ aItem.message }}
 
 if appflash.length > 0
-	script(type='text/javascript')
-		| var alertsData = 
-		!= JSON.stringify(appflash)
-		| ;
+	script(type='text/javascript').
+		var alertsData = !{JSON.stringify(appflash)};
 else
-	script(type='text/javascript')
-		| var alertsData = [];
+	script(type='text/javascript').
+		var alertsData = [];

+ 28 - 0
views/modals/editor-video.pug

@@ -0,0 +1,28 @@
+
+.modal#modal-editor-video
+	.modal-background
+	.modal-container
+		.modal-content
+			header.is-green Insert Video Player
+			section
+				label.label Enter the link to the video to be embedded:
+				p.control.is-fullwidth
+					input.input(type='text', placeholder='https://www.youtube.com/watch?v=xxxxxxxxxxx', v-model='link')
+					span.help.is-red.is-hidden This URL is invalid or not supported!
+				.note The following are supported:
+					ul
+						li
+							i.icon-youtube-play
+							span Youtube
+						li
+							i.icon-vimeo
+							span Vimeo
+						li
+							i.icon-film
+							span Dailymotion
+						li
+							i.icon-video
+							span Any standard MP4 file
+			footer
+				a.button.is-grey.is-outlined(v-on:click="cancel") Discard
+				a.button.is-green(v-on:click="insertVideo") Insert Video

+ 2 - 0
views/pages/create.pug

@@ -22,4 +22,6 @@ block content
 	include ../modals/create-discard.pug
 	include ../modals/editor-link.pug
 	include ../modals/editor-image.pug
+	include ../modals/editor-file.pug
+	include ../modals/editor-video.pug
 	include ../modals/editor-codeblock.pug

+ 1 - 0
views/pages/edit.pug

@@ -23,4 +23,5 @@ block content
 	include ../modals/editor-link.pug
 	include ../modals/editor-image.pug
 	include ../modals/editor-file.pug
+	include ../modals/editor-video.pug
 	include ../modals/editor-codeblock.pug

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels