@diplodoc/transform
Version:
A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML
152 lines (125 loc) • 5.25 kB
text/typescript
// Process @[youtube](youtubeVideoID)
// Process @[vimeo](vimeoVideoID)
// Process @[vine](vineVideoID)
// Process @[prezi](preziID)
// Process @[osf](guid)
// Process @[yandex](videoID)
// Process @[vk](videoID)
// Process @[rutube](videoID)
// Process @[url](fullLink)
import type MarkdownIt from 'markdown-it';
// eslint-disable-next-line no-duplicate-imports
import type {PluginWithOptions} from 'markdown-it';
import type ParserInline from 'markdown-it/lib/parser_inline';
import type Renderer from 'markdown-it/lib/renderer';
import type {VideoFullOptions, VideoPluginOptions, VideoToken} from './types';
import {append} from '../utils';
import {parseVideoUrl} from './parsers';
import {VideoService, defaults} from './const';
// eslint-disable-next-line valid-jsdoc
/**
* Video plugin for markdown-it.
* Forked from https://github.com/CenterForOpenScience/markdown-it-video/tree/0.6.3
*/
const video: PluginWithOptions<VideoPluginOptions> = (md, options) => {
const theOptions: VideoFullOptions = {...defaults, ...options};
const theMd = md;
theMd.renderer.rules.video = tokenizeVideo(theMd, theOptions);
theMd.inline.ruler.before('emphasis', 'video', videoEmbed(theMd, theOptions));
};
function tokenizeVideo(md: MarkdownIt, options: VideoFullOptions): Renderer.RenderRule {
return (tokens, idx) => {
const videoID = md.utils.escapeHtml((tokens[idx] as VideoToken).videoID);
const service = md.utils.escapeHtml((tokens[idx] as VideoToken).service).toLowerCase();
const 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,}/;
let num;
if (service === VideoService.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>'
);
}
const {width, height} = options[service as VideoService];
return videoID === ''
? ''
: `<div class="embed-responsive embed-responsive-16by9"><iframe` +
` class="embed-responsive-item ${service}-player"` +
` type="text/html" width="${width}" height="${height}"` +
` src="${options.videoUrl(service, videoID, options)}"` +
` frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>`;
};
}
const EMBED_REGEX = /@\[([a-zA-Z]*?)]\([\s]*(.*?)[\s]*[)]/im;
function videoEmbed(md: MarkdownIt, _options: VideoFullOptions): ParserInline.RuleInline {
return (state, silent) => {
const 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] || 'url';
const parsed = parseVideoUrl(service, match[2]);
if (parsed === false) {
return false;
}
const [videoID, csp] = parsed;
const serviceStart = oldPos + 2;
const 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;
// @ts-expect-error
theState.service = theState.src.slice(serviceStart, serviceEnd);
const newState = new theState.md.inline.State(service, theState.md, theState.env, []);
newState.md.inline.tokenize(newState);
const token = theState.push('video', '', 0) as VideoToken;
token.videoID = videoID;
token.service = service;
token.level = theState.level;
}
if (service === 'url') {
theState.pos = theState.src.indexOf(')', theState.pos) + 1;
} else {
theState.pos += theState.src.indexOf(')', theState.pos);
}
if (csp) {
state.env.meta ??= {};
append(state.env.meta, 'csp', csp);
}
return true;
};
}
export = video;