forked from tatsy/markdown-it-imsize
-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathindex.js
239 lines (204 loc) · 8.17 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Process @[youtube](youtubeVideoID)
// Process @[vimeo](vimeoVideoID)
// Process @[vine](vineVideoID)
// Process @[prezi](preziID)
// Process @[osf](guid)
const ytRegex = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
function youtubeParser(url) {
const match = url.match(ytRegex);
return match && match[7].length === 11 ? match[7] : url;
}
/* eslint-disable max-len */
const vimeoRegex = /(?:http:|https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)/;
/* eslint-enable max-len */
function vimeoParser(url) {
const match = url.match(vimeoRegex);
return match && typeof match[3] === 'string' ? match[3] : url;
}
const vineRegex = /^http(?:s?):\/\/(?:www\.)?vine\.co\/v\/([a-zA-Z0-9]{1,13}).*/;
function vineParser(url) {
const match = url.match(vineRegex);
return match && match[1].length === 11 ? match[1] : url;
}
const preziRegex = /^https:\/\/prezi.com\/(.[^/]+)/;
function preziParser(url) {
const match = url.match(preziRegex);
return match ? match[1] : url;
}
// TODO: Write regex for staging and local servers.
const mfrRegex = /^http(?:s?):\/\/(?:www\.)?mfr\.osf\.io\/render\?url=http(?:s?):\/\/osf\.io\/([a-zA-Z0-9]{1,5})\/\?action=download/;
function mfrParser(url) {
const match = url.match(mfrRegex);
return match ? match[1] : url;
}
const EMBED_REGEX = /@\[([a-zA-Z].+)]\([\s]*(.*?)[\s]*[)]/im;
function videoEmbed(md, options) {
function videoReturn(state, silent) {
var serviceEnd;
var serviceStart;
var token;
var videoID;
var theState = state;
const oldPos = state.pos;
if (state.src.charCodeAt(oldPos) !== 0x40/* @ */ ||
state.src.charCodeAt(oldPos + 1) !== 0x5B/* [ */) {
return false;
}
const match = EMBED_REGEX.exec(state.src.slice(state.pos, state.src.length));
if (!match || match.length < 3) {
return false;
}
const service = match[1];
videoID = match[2];
const serviceLower = service.toLowerCase();
if (serviceLower === 'youtube') {
videoID = youtubeParser(videoID);
} else if (serviceLower === 'vimeo') {
videoID = vimeoParser(videoID);
} else if (serviceLower === 'vine') {
videoID = vineParser(videoID);
} else if (serviceLower === 'prezi') {
videoID = preziParser(videoID);
} else if (serviceLower === 'osf') {
videoID = mfrParser(videoID);
} else if (!options[serviceLower]) {
return false;
}
// If the videoID field is empty, regex currently make it the close parenthesis.
if (videoID === ')') {
videoID = '';
}
serviceStart = oldPos + 2;
serviceEnd = md.helpers.parseLinkLabel(state, oldPos + 1, false);
//
// We found the end of the link, and know for a fact it's a valid link;
// so all that's left to do is to call tokenizer.
//
if (!silent) {
theState.pos = serviceStart;
theState.service = theState.src.slice(serviceStart, serviceEnd);
const newState = new theState.md.inline.State(service, theState.md, theState.env, []);
newState.md.inline.tokenize(newState);
token = theState.push('video', '');
token.videoID = videoID;
token.service = service;
token.url = match[2];
token.level = theState.level;
}
theState.pos += theState.src.indexOf(')', theState.pos);
return true;
}
return videoReturn;
}
function extractVideoParameters(url) {
const parameterMap = new Map();
const params = url.replace(/&/gi, '&').split(/[#?&]/);
if (params.length > 1) {
for (let i = 1; i < params.length; i += 1) {
const keyValue = params[i].split('=');
if (keyValue.length > 1) parameterMap.set(keyValue[0], keyValue[1]);
}
}
return parameterMap;
}
function videoUrl(service, videoID, url, options) {
switch (service) {
case 'youtube': {
const parameters = extractVideoParameters(url);
if (options.youtube.parameters) {
Object.keys(options.youtube.parameters).forEach((key) => {
parameters.set(key, options.youtube.parameters[key]);
});
}
// Start time parameter can have the format t=0m10s or t=<time_in_seconds> in share URLs,
// but in embed URLs the parameter must be called 'start' and time must be in seconds
const timeParameter = parameters.get('t');
if (timeParameter !== undefined) {
let startTime = 0;
const timeParts = timeParameter.match(/[0-9]+/g);
let j = 0;
while (timeParts.length > 0) {
/* eslint-disable no-restricted-properties */
startTime += Number(timeParts.pop()) * Math.pow(60, j);
/* eslint-enable no-restricted-properties */
j += 1;
}
parameters.set('start', startTime);
parameters.delete('t');
}
parameters.delete('v');
parameters.delete('feature');
parameters.delete('origin');
const parameterArray = Array.from(parameters, p => p.join('='));
const parameterPos = videoID.indexOf('?');
let finalUrl = 'https://www.youtube';
if (options.youtube.nocookie || url.indexOf('youtube-nocookie.com') > -1) finalUrl += '-nocookie';
finalUrl += '.com/embed/' + (parameterPos > -1 ? videoID.substr(0, parameterPos) : videoID);
if (parameterArray.length > 0) finalUrl += '?' + parameterArray.join('&');
return finalUrl;
}
case 'vimeo':
return 'https://player.vimeo.com/video/' + videoID;
case 'vine':
return 'https://vine.co/v/' + videoID + '/embed/' + options.vine.embed;
case 'prezi':
return 'https://prezi.com/embed/' + videoID +
'/?bgcolor=ffffff&lock_to_path=0&autoplay=0&autohide_ctrls=0&' +
'landing_data=bHVZZmNaNDBIWnNjdEVENDRhZDFNZGNIUE43MHdLNWpsdFJLb2ZHanI5N1lQVHkxSHFxazZ0UUNCRHloSXZROHh3PT0&' +
'landing_sign=1kD6c0N6aYpMUS0wxnQjxzSqZlEB8qNFdxtdjYhwSuI';
case 'osf':
return 'https://mfr.osf.io/render?url=https://osf.io/' + videoID + '/?action=download';
default:
return service;
}
}
function tokenizeVideo(md, options) {
function tokenizeReturn(tokens, idx) {
const videoID = md.utils.escapeHtml(tokens[idx].videoID);
const service = md.utils.escapeHtml(tokens[idx].service).toLowerCase();
var checkUrl = /http(?:s?):\/\/(?:www\.)?[a-zA-Z0-9-:.]{1,}\/render(?:\/)?[a-zA-Z0-9.&;?=:%]{1,}url=http(?:s?):\/\/[a-zA-Z0-9 -:.]{1,}\/[a-zA-Z0-9]{1,5}\/\?[a-zA-Z0-9.=:%]{1,}/;
var num;
if (service === 'osf' && videoID) {
num = Math.random() * 0x10000;
if (videoID.match(checkUrl)) {
return '<div id="' + num + '" class="mfr mfr-file"></div><script>' +
'$(document).ready(function () {new mfr.Render("' + num + '", "' + videoID + '");' +
' }); </script>';
}
return '<div id="' + num + '" class="mfr mfr-file"></div><script>' +
'$(document).ready(function () {new mfr.Render("' + num + '", "https://mfr.osf.io/' +
'render?url=https://osf.io/' + videoID + '/?action=download%26mode=render");' +
' }); </script>';
}
return videoID === '' ? '' :
'<div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item ' +
service + '-player" type="text/html" width="' + (options[service].width) +
'" height="' + (options[service].height) +
'" src="' + options.url(service, videoID, tokens[idx].url, options) +
'" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>';
}
return tokenizeReturn;
}
const defaults = {
url: videoUrl,
youtube: { width: 640, height: 390, nocookie: false },
vimeo: { width: 500, height: 281 },
vine: { width: 600, height: 600, embed: 'simple' },
prezi: { width: 550, height: 400 },
osf: { width: '100%', height: '100%' },
};
module.exports = function videoPlugin(md, options) {
var theOptions = options;
var theMd = md;
if (theOptions) {
Object.keys(defaults).forEach(function checkForKeys(key) {
if (typeof theOptions[key] === 'undefined') {
theOptions[key] = defaults[key];
}
});
} else {
theOptions = defaults;
}
theMd.renderer.rules.video = tokenizeVideo(theMd, theOptions);
theMd.inline.ruler.before('emphasis', 'video', videoEmbed(theMd, theOptions));
};