Video player modal + provider match

This commit is contained in:
NGPixel 2017-01-02 22:20:24 -05:00
parent dab4a97094
commit 4625a302f6
15 changed files with 168 additions and 43 deletions

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,6 +58,31 @@ mkdown.renderer.rules.emoji = function(token, idx) {
return '<i class="twa twa-' + _.replace(token[idx].markup, /_/g, '-') + '"></i>'; 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 * 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(); output = cr.html();
return output; return output;

View File

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

View File

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

View File

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

View File

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

View File

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