Video player modal + provider match
This commit is contained in:
parent
dab4a97094
commit
4625a302f6
@ -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
@ -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
|
||||||
|
49
client/js/components/editor-video.js
Normal file
49
client/js/components/editor-video.js
Normal 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
@ -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,
|
||||||
|
@ -196,7 +196,7 @@ gulp.task('dev', function() {
|
|||||||
|
|
||||||
return run('default');
|
return run('default');
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TASK - Creates deployment packages
|
* TASK - Creates deployment packages
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -222,7 +222,7 @@ module.exports = {
|
|||||||
|
|
||||||
destFileStream.on('finish', () => {
|
destFileStream.on('finish', () => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
});
|
||||||
|
|
||||||
rq.pipe(destFileStream);
|
rq.pipe(destFileStream);
|
||||||
|
|
||||||
|
@ -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 = [];
|
28
views/modals/editor-video.pug
Normal file
28
views/modals/editor-video.pug
Normal 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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user