Skip to content

Commit 4625a30

Browse files
committed
Video player modal + provider match
1 parent dab4a97 commit 4625a30

File tree

15 files changed

+168
-43
lines changed

15 files changed

+168
-43
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
- [ ] Links
5050
- [x] Image Selection modal
5151
- [x] File Selection modal
52+
- [x] Video player
5253
- [x] Inline Code
5354
- [x] Code Editor modal
5455
- [ ] Table Editor

assets/css/app.css

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/js/app.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/js/components/alerts.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Alerts {
4242

4343
let nAlert = _.defaults(options, {
4444
_uid: self.uidNext,
45-
class: 'is-info',
45+
class: 'info',
4646
message: '---',
4747
sticky: false,
4848
title: '---'
@@ -68,7 +68,7 @@ class Alerts {
6868
*/
6969
pushError(title, message) {
7070
this.push({
71-
class: 'is-danger',
71+
class: 'error',
7272
message,
7373
sticky: false,
7474
title
@@ -83,7 +83,7 @@ class Alerts {
8383
*/
8484
pushSuccess(title, message) {
8585
this.push({
86-
class: 'is-success',
86+
class: 'success',
8787
message,
8888
sticky: false,
8989
title

client/js/components/editor-video.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
const videoRules = {
3+
'youtube': new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
4+
'vimeo': new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
5+
'dailymotion': new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i')
6+
};
7+
8+
// Vue Video instance
9+
10+
let vueVideo = new Vue({
11+
el: '#modal-editor-video',
12+
data: {
13+
link: ''
14+
},
15+
methods: {
16+
open: (ev) => {
17+
$('#modal-editor-video').addClass('is-active');
18+
$('#modal-editor-video input').focus();
19+
},
20+
cancel: (ev) => {
21+
mdeModalOpenState = false;
22+
$('#modal-editor-video').removeClass('is-active');
23+
vueVideo.link = '';
24+
},
25+
insertVideo: (ev) => {
26+
27+
if(mde.codemirror.doc.somethingSelected()) {
28+
mde.codemirror.execCommand('singleSelection');
29+
}
30+
31+
// Guess video type
32+
33+
let videoType = _.findKey(videoRules, (vr) => {
34+
return vr.test(vueVideo.link);
35+
});
36+
if(_.isNil(videoType)) {
37+
videoType = 'video';
38+
}
39+
40+
// Insert video tag
41+
42+
let videoText = '[video](' + vueVideo.link + '){.' + videoType + '}\n';
43+
44+
mde.codemirror.doc.replaceSelection(videoText);
45+
vueVideo.cancel();
46+
47+
}
48+
}
49+
});

client/js/components/editor.js

+23-22
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ if($('#mk-editor').length === 1) {
1414

1515
//=include editor-image.js
1616
//=include editor-file.js
17+
//=include editor-video.js
1718
//=include editor-codeblock.js
1819

1920
var mde = new SimpleMDE({
@@ -70,7 +71,7 @@ if($('#mk-editor').length === 1) {
7071
{
7172
name: "unordered-list",
7273
action: SimpleMDE.toggleUnorderedList,
73-
className: "icon-list-ul",
74+
className: "icon-th-list",
7475
title: "Bullet List",
7576
},
7677
{
@@ -98,7 +99,7 @@ if($('#mk-editor').length === 1) {
9899
vueImage.open();
99100
}
100101
},
101-
className: "icon-image3",
102+
className: "icon-image",
102103
title: "Insert Image",
103104
},
104105
{
@@ -108,15 +109,15 @@ if($('#mk-editor').length === 1) {
108109
vueFile.open();
109110
}
110111
},
111-
className: "icon-file-text-o",
112+
className: "icon-paper",
112113
title: "Insert File",
113114
},
114115
{
115116
name: "video",
116117
action: (editor) => {
117-
/*if(!mdeModalOpenState) {
118-
vueFile.open();
119-
}*/
118+
if(!mdeModalOpenState) {
119+
vueVideo.open();
120+
}
120121
},
121122
className: "icon-video-camera2",
122123
title: "Insert Video Player",
@@ -180,21 +181,6 @@ if($('#mk-editor').length === 1) {
180181

181182
//-> Save
182183

183-
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
184-
saveCurrentDocument(ev);
185-
});
186-
187-
$(window).bind('keydown', (ev) => {
188-
if (ev.ctrlKey || ev.metaKey) {
189-
switch (String.fromCharCode(ev.which).toLowerCase()) {
190-
case 's':
191-
ev.preventDefault();
192-
saveCurrentDocument(ev);
193-
break;
194-
}
195-
}
196-
});
197-
198184
let saveCurrentDocument = (ev) => {
199185
$.ajax(window.location.href, {
200186
data: {
@@ -211,6 +197,21 @@ if($('#mk-editor').length === 1) {
211197
}, (rXHR, rStatus, err) => {
212198
alerts.pushError('Something went wrong', 'Save operation failed.');
213199
});
214-
}
200+
};
201+
202+
$('.btn-edit-save, .btn-create-save').on('click', (ev) => {
203+
saveCurrentDocument(ev);
204+
});
205+
206+
$(window).bind('keydown', (ev) => {
207+
if (ev.ctrlKey || ev.metaKey) {
208+
switch (String.fromCharCode(ev.which).toLowerCase()) {
209+
case 's':
210+
ev.preventDefault();
211+
saveCurrentDocument(ev);
212+
break;
213+
}
214+
}
215+
});
215216

216217
}

controllers/pages.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ router.put('/edit/*', (req, res, next) => {
4343
let safePath = entries.parsePath(_.replace(req.path, '/edit', ''));
4444

4545
entries.update(safePath, req.body.markdown).then(() => {
46-
res.json({
46+
return res.json({
4747
ok: true
48-
});
48+
}) || true;
4949
}).catch((err) => {
5050
res.json({
5151
ok: false,
@@ -105,9 +105,9 @@ router.put('/create/*', (req, res, next) => {
105105
let safePath = entries.parsePath(_.replace(req.path, '/create', ''));
106106

107107
entries.create(safePath, req.body.markdown).then(() => {
108-
res.json({
108+
return res.json({
109109
ok: true
110-
});
110+
}) || true;
111111
}).catch((err) => {
112112
res.json({
113113
ok: false,

gulpfile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ gulp.task('dev', function() {
196196

197197
return run('default');
198198

199-
})
199+
});
200200

201201
/**
202202
* TASK - Creates deployment packages

libs/local.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,9 @@ module.exports = {
158158
/**
159159
* Check if filename is valid and unique
160160
*
161-
* @param {String} f The filename
162-
* @param {String} fld The containing folder
161+
* @param {String} f The filename
162+
* @param {String} fld The containing folder
163+
* @param {boolean} isImage Indicates if image
163164
* @return {Promise<String>} Promise of the accepted filename
164165
*/
165166
validateUploadsFilename(f, fld, isImage) {

libs/markdown.js

+44
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,31 @@ mkdown.renderer.rules.emoji = function(token, idx) {
5858
return '<i class="twa twa-' + _.replace(token[idx].markup, /_/g, '-') + '"></i>';
5959
};
6060

61+
// Video rules
62+
63+
const videoRules = [
64+
{
65+
selector: 'a.youtube',
66+
regexp: new RegExp(/(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/, 'i'),
67+
output: '<iframe width="640" height="360" src="https://www.youtube.com/embed/{0}?rel=0" frameborder="0" allowfullscreen></iframe>'
68+
},
69+
{
70+
selector: 'a.vimeo',
71+
regexp: new RegExp(/vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|)(\d+)(?:$|\/|\?)/, 'i'),
72+
output: '<iframe src="https://player.vimeo.com/video/{0}" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
73+
},
74+
{
75+
selector: 'a.dailymotion',
76+
regexp: new RegExp(/(?:dailymotion\.com(?:\/embed)?(?:\/video|\/hub)|dai\.ly)\/([0-9a-z]+)(?:[\-_0-9a-zA-Z]+(?:#video=)?([a-z0-9]+)?)?/, 'i'),
77+
output: '<iframe width="640" height="360" src="//www.dailymotion.com/embed/video/{0}?endscreen-enable=false" frameborder="0" allowfullscreen></iframe>'
78+
},
79+
{
80+
selector: 'a.video',
81+
regexp: false,
82+
output: '<video width="640" height="360" controls preload="metadata"><source src="{0}" type="video/mp4"></video>'
83+
}
84+
]
85+
6186
/**
6287
* Parse markdown content and build TOC tree
6388
*
@@ -204,6 +229,25 @@ const parseContent = (content) => {
204229
});
205230
});
206231

232+
// Replace video links with embeds
233+
234+
_.forEach(videoRules, (vrule) => {
235+
cr(vrule.selector).each((i, elm) => {
236+
let originLink = cr(elm).attr('href');
237+
if(vrule.regexp) {
238+
let vidMatches = originLink.match(vrule.regexp);
239+
if((vidMatches && _.isArray(vidMatches))) {
240+
vidMatches = _.filter(vidMatches, (f) => {
241+
return f && _.isString(f);
242+
});
243+
originLink = _.last(vidMatches);
244+
}
245+
}
246+
let processedLink = _.replace(vrule.output, '{0}', originLink);
247+
cr(elm).replaceWith(processedLink);
248+
});
249+
});
250+
207251
output = cr.html();
208252

209253
return output;

libs/uploads.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ module.exports = {
222222

223223
destFileStream.on('finish', () => {
224224
resolve(true);
225-
})
225+
});
226226

227227
rq.pipe(destFileStream);
228228

views/common/alerts.pug

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
#alerts
22
ul
33
template(v-for="aItem in children", track-by='_uid')
4-
.notification(v-bind:class='aItem.class')
4+
li(v-bind:class='aItem.class')
55
button.delete(v-on:click='acknowledge(aItem._uid)')
66
h3 {{ aItem.title }}
77
span {{ aItem.message }}
88

99
if appflash.length > 0
10-
script(type='text/javascript')
11-
| var alertsData =
12-
!= JSON.stringify(appflash)
13-
| ;
10+
script(type='text/javascript').
11+
var alertsData = !{JSON.stringify(appflash)};
1412
else
15-
script(type='text/javascript')
16-
| var alertsData = [];
13+
script(type='text/javascript').
14+
var alertsData = [];

views/modals/editor-video.pug

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
.modal#modal-editor-video
3+
.modal-background
4+
.modal-container
5+
.modal-content
6+
header.is-green Insert Video Player
7+
section
8+
label.label Enter the link to the video to be embedded:
9+
p.control.is-fullwidth
10+
input.input(type='text', placeholder='https://www.youtube.com/watch?v=xxxxxxxxxxx', v-model='link')
11+
span.help.is-red.is-hidden This URL is invalid or not supported!
12+
.note The following are supported:
13+
ul
14+
li
15+
i.icon-youtube-play
16+
span Youtube
17+
li
18+
i.icon-vimeo
19+
span Vimeo
20+
li
21+
i.icon-film
22+
span Dailymotion
23+
li
24+
i.icon-video
25+
span Any standard MP4 file
26+
footer
27+
a.button.is-grey.is-outlined(v-on:click="cancel") Discard
28+
a.button.is-green(v-on:click="insertVideo") Insert Video

views/pages/create.pug

+2
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@ block content
2222
include ../modals/create-discard.pug
2323
include ../modals/editor-link.pug
2424
include ../modals/editor-image.pug
25+
include ../modals/editor-file.pug
26+
include ../modals/editor-video.pug
2527
include ../modals/editor-codeblock.pug

views/pages/edit.pug

+1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ block content
2323
include ../modals/editor-link.pug
2424
include ../modals/editor-image.pug
2525
include ../modals/editor-file.pug
26+
include ../modals/editor-video.pug
2627
include ../modals/editor-codeblock.pug

0 commit comments

Comments
 (0)