Skip to content

Instantly share code, notes, and snippets.

@Snugug
Last active April 7, 2020 07:32
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Snugug/9203fd0dc5e00d8ce799 to your computer and use it in GitHub Desktop.
Save Snugug/9203fd0dc5e00d8ce799 to your computer and use it in GitHub Desktop.
Image Renderer for Marked (https://github.com/chjj/marked/) to transform it into a media embed tool

I've always kinda viewed Markdown's image embed more as a media embed than strictly an image embed, so I decided to transform the image rendering in Marked into a generic media render. Right now it supports three types of embeds; the standard image embeds, YouTube/Vimeo embeds, and HTML5 video embeds with .webm and .mp4 file extensions.

  • Image usage is standard Markdown
  • YouTube/Vimeo usage: Use a YouTube/Vimeo video's URL and it'll generate a YouTube/Vimeo <iframe> embed
  • HTML5 Video: Use either a relative or absolute URL that ends with .webm or .mp4 and it will create a <video> tag for you. The text inside brackets will be used if HTML5 Video is unavailable. If an image URL in quotes is provided after the video URL, it will be used for the video's poster. Videos have controls on. If the video is relative and it's a .webm file, it will attempt to find a corisponding .mp4 file and include that as a secondary source. A base path to prepend to the the relative path it is checking for can be set using the localVideoPath option.

YouTube/Vimeo Embed

Code - YouTube

![Epic Rap Battles of History](https://www.youtube.com/watch?v=_wYtG7aQTHA)

Output - YouTube

<p><iframe width="560" height="315" src="//www.youtube.com/embed/_wYtG7aQTHA" frameborder="0" allowfullscreen></iframe></p>

Code - Vimeo

![SassConf Sizzle](http://vimeo.com/channels/sassconf2013/90602247)

Output - Vimeo

<p><iframe src="//player.vimeo.com/video/90602247" width="560" height="315" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></p>

HTML5 Video

Code

![Shake it Off](videos/shake-it-off.webm "images/shake-it-off.png")

Output

<p><video controls poster="images/shake-it-off.png"><source src="videos/shake-it-off.webm" type="video/webm"><source src="videos/shake-it-off.mp4" type="video/mp4">Shake it Off</video></p>
var marked = require('marked'),
fs = require('fs'),
path = require('path'),
URI = require('URIjs'),
renderer = new marked.Renderer(),
file = fs.readFileSync('./foo.md', 'utf8');
// Set Marked's rendereer to the custom renderer
// optional `localVideoPath` option will be used to to find fallback videos for `.webm`. Defaults to ''
marked.setOptions({
'renderer': renderer,
'localVideoPath': './'
});
renderer.image = function (href, title, text) {
var ext = path.extname(href),
uri = URI(href),
localPath = this.options.localVideoPath ? this.options.localVideoPath : '',
alt,
out;
// YouTube Embed
if (uri.hostname() === 'www.youtube.com') {
out = '<iframe width="560" height="315" src="//www.youtube.com/embed/' + uri.query().substring(2) + '" frameborder="0" allowfullscreen></iframe>';
}
// Vimeo Embed
else if (uri.hostname() === 'vimeo.com') {
out = '<iframe src="//player.vimeo.com/video/' + uri.path().split('/').pop() + '" width="560" height="315" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
}
// Image output
else if (ext !== '.webm' && ext !== '.mp4') {
out = '<img src="' + href + '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
}
out += this.options.xhtml ? '/>' : '>';
}
// Video Output
else {
out = '<video controls';
// If a poster exists, add it
if (title) {
out += ' poster="' + title + '"';
}
out += '>';
// Video Source
out += '<source src="' + href + '" type="video/' + ext.replace('.', '') + '">'
// If the file is local /and/ it's a .webm, see if the .mp4 version is available
if (uri.protocol() === '' && ext === '.webm') {
alt = href.slice(0, -5) + '.mp4';
if (fs.existsSync(localPath + alt)) {
out += '<source src="' + alt + '" type="video/mp4">'
}
}
// Fallback Text
if (text) {
out += text;
}
out += '</video>';
}
return out;
}
@ircmaxell
Copy link

There is a XSS vulnerability here, everywhere you post user-data into the generated HTML.

Instead, you should html encode them properly first. I'm not sure what libraries are included with marked (definitely check there first), but as a fallback:

function escapeHtml(str) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
};

@Snugug
Copy link
Author

Snugug commented Dec 29, 2014

Marked handles the escaping for me (their escape function)

Also, this is server-side code (although in theory could be used on the client side)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment