Skip to content
This repository was archived by the owner on Mar 7, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "osmtm/static/js/lib/leaflet-control-osm-geocoder"]
path = osmtm/static/js/lib/leaflet-control-osm-geocoder
url = https://github.com/k4r573n/leaflet-control-osm-geocoder.git
[submodule "osmtm/static/js/lib/showdown"]
path = osmtm/static/js/lib/showdown
url = https://github.com/showdownjs/showdown.git
7 changes: 5 additions & 2 deletions osmtm/mako_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
User,
)

from markdown_extensions import OEmbedExtension

amp = re.compile('&')

md = markdown.Markdown(extensions=[OEmbedExtension()])


def markdown_filter(text):
''' Mako filter for markdown and bleach
'''
cleaned = bleach.clean(text, strip=True)
parsed = markdown.markdown(cleaned)
parsed = md.convert(cleaned)
return re.sub(amp, '&', parsed)


Expand All @@ -24,7 +28,6 @@ def convert_mentions(request):
''' Mako filter to convert any @id mention to link to user profile
'''
def d(text):

def repl(val):
user_id = val.group()[1:]
user = DBSession.query(User).get(user_id)
Expand Down
77 changes: 77 additions & 0 deletions osmtm/markdown_extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from markdown import Extension
from markdown.inlinepatterns import Pattern
import oembed

DEFAULT_ENDPOINTS = [
# Youtube
oembed.OEmbedEndpoint('http://www.youtube.com/oembed', [
'https?://(*.)?youtube.com/*',
'https?://youtu.be/*',
]),

# Flickr
oembed.OEmbedEndpoint('http://www.flickr.com/services/oembed/', [
'https?://*.flickr.com/*',
]),

# Vimeo
oembed.OEmbedEndpoint('http://vimeo.com/api/oembed.json', [
'https?://vimeo.com/*',
]),
]

OEMBED_LINK_RE = r'\!\[([^\]]*)\]\(((?:https?:)?//[^\)]*)' \
r'(?<!png)(?<!jpg)(?<!jpeg)(?<!gif)\)'


class OEmbedLinkPattern(Pattern):
def __init__(self, pattern, markdown_instance=None, oembed_consumer=None):
Pattern.__init__(self, pattern, markdown_instance)
self.consumer = oembed_consumer

def handleMatch(self, match):
html = self.get_oembed_html_for_match(match)
if html is None:
return None
else:
html = "<figure class=\"oembed\">%s</figure>" % html
placeholder = self.markdown.htmlStash.store(html, True)
return placeholder

def get_oembed_html_for_match(self, match):
url = match.group(3).strip()
try:
response = self.consumer.embed(url)
except oembed.OEmbedNoEndpoint:
return None
else:
return response['html']


class OEmbedExtension(Extension):
def __init__(self, **kwargs):
self.config = {
'allowed_endpoints': [
DEFAULT_ENDPOINTS,
"A list of oEmbed endpoints to allow. Defaults to "
"endpoints.DEFAULT_ENDPOINTS"
],
}
super(OEmbedExtension, self).__init__(**kwargs)
self.oembed_consumer = self.prepare_oembed_consumer()

def extendMarkdown(self, md, md_globals):
link_pattern = OEmbedLinkPattern(OEMBED_LINK_RE, md,
self.oembed_consumer)
md.inlinePatterns.add('oembed_link', link_pattern, '<image_link')

def prepare_oembed_consumer(self):
allowed_endpoints = self.getConfig('allowed_endpoints',
DEFAULT_ENDPOINTS)
consumer = oembed.OEmbedConsumer()

if allowed_endpoints:
for endpoint in allowed_endpoints:
consumer.addEndpoint(endpoint)

return consumer
9 changes: 9 additions & 0 deletions osmtm/static/html/markdown_quick_ref.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,12 @@ <h2><span class="mw-headline" id="Sub-heading">Sub-heading</span></h2>
<img src="http://www.openstreetmap.org/assets/osm_logo-9ad4e5dd683af63e657633d3344f0a4d.png" alt="alternate">
</div>
</div>

<div class="row">
<div class="col-md-6">
<pre>![video](http://www.youtube.com/watch?v=StTqXEQ2l-Y)</pre>
</div>
<div class="col-md-6">
<pre>oEmbed link, currently support Youtube, Flickr and Vimeo. </pre>
</div>
</div>
1 change: 1 addition & 0 deletions osmtm/static/js/lib/showdown
Submodule showdown added at f4d50d
105 changes: 105 additions & 0 deletions osmtm/static/js/lib/showdown-youtube.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Youtube Extension.
* Uses image syntax to embed videos
* Usage:
* ![youtube video][http://youtu.be/dQw4w9WgXcQ]
*
* or
*
* ![youtube video][1]
* [1]: http://youtu.be/dQw4w9WgXcQ
*/
(function (extension) {
'use strict';

if (showdown) {
// global (browser or nodejs global)
extension(showdown);
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['showdown'], extension);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = extension(require('showdown'));
} else {
// showdown was not found so we throw
throw Error('Could not find showdown library');
}

}(function (showdown) {

var svg =
'<div class="youtube-preview" style="width:%2; height:%3; background-color:#333; position:relative;">' +
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
' width="100" height="70" viewBox="0 0 100 70"' +
' style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">' +
' <defs>' +
' <linearGradient id="grad1" x1="0%" x2="0%" y1="0%" x2="0%" y2="100%">' +
' <stop offset="0%" style="stop-color:rgb(229,45,49);stop-opacity:1" />' +
' <stop offset="100%" style="stop-color:rgb(191,23,29);stop-opacity:1" />' +
' </linearGradient>' +
' </defs>' +
' <rect width="100%" height="100%" rx="26" fill="url(#grad1)"/>' +
' <polygon points="35,20 70,35 35,50" fill="#fff"/>' +
' <polygon points="35,20 70,35 64,37 35,21" fill="#e8e0e0"/>' +
'</svg>' +
'<div style="text-align:center; padding-top:10px; color:#fff"><a href="//youtu.be/%1">youtu.be/%1</a></div>' +
'</div>',
iframe = '<iframe width="%2" height="%3" src="//www.youtube.com/embed/%1?rel=0" frameborder="0" allowfullscreen></iframe>',
img = '<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=" width="%2" height="%3">',
imgRegex = /(?:<p>)?<img.*?src="(.+?)"(.*?)\/?>(?:<\/p>)?/gi,
fullLinkRegex = /(?:(?:https?:)?(?:\/\/)?)(?:(?:www)?\.)?youtube\.(?:.+?)\/(?:(?:watch\?v=)|(?:embed\/))([a-zA-Z0-9_-]{11})/i,
shortLinkRegex = /(?:(?:https?:)?(?:\/\/)?)?youtu\.be\/([a-zA-Z0-9_-]{11})/i;

function parseDimensions(rest) {
var width,
height,
d;

if (rest) {
width = (d = /width="(.+?)"/.exec(rest)) ? d[1] : '420';
height = (d = /height="(.+?)"/.exec(rest)) ? d[1] : '315';
}

// add units so they can be used in css
if (/^\d+$/gm.exec(width)) {
width += 'px';
}
if (/^\d+$/gm.exec(height)) {
height += 'px';
}

return {
width: width,
height: height
};
}

/**
* Replace with video iframes
*/
showdown.extension('youtube', function () {
return [
{
// It's a bit hackish but we let the core parsers replace the reference image for an image tag
// then we replace the full img tag in the output with our iframe
type: 'output',
filter: function (text, converter, options) {
var tag = iframe;
if (options.smoothLivePreview) {
tag = (options.youtubeUseSimpleImg) ? img : svg;
}
return text.replace(imgRegex, function (match, url, rest) {
var d = parseDimensions(rest),
m;
if ((m = shortLinkRegex.exec(url)) || (m = fullLinkRegex.exec(url))) {
return tag.replace(/%1/g, m[1]).replace('%2', d.width).replace('%3', d.height);
} else {
return match;
}
});
}
}
];
});
}));
4 changes: 3 additions & 1 deletion osmtm/templates/base.mako
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
<link rel="stylesheet" href="${request.static_url('osmtm:static/css/main.css')}">
<link rel="stylesheet" href="${request.static_url('osmtm:static/js/lib/leaflet.css')}">
<script src="${request.static_url('osmtm:static/js/lib/jquery-1.7.2.min.js')}"></script>
<script src="${request.static_url('osmtm:static/js/lib/showdown.js')}"></script>
<script src="${request.static_url('osmtm:static/js/lib/showdown/dist/showdown.js')}"></script>
<script src="${request.static_url('osmtm:static/js/lib/showdown-youtube.js')}"></script>
<script src="${request.static_url('osmtm:static/js/lib/jquery-timeago/jquery.timeago.js')}"></script>
<%
timeago_locale_baseurl = 'osmtm:static/js/lib/jquery-timeago/locales/jquery.timeago.%s.js'
Expand All @@ -24,6 +25,7 @@
<script src="${request.static_url('osmtm:static/js/lib/sammy-latest.min.js')}"></script>
<script src="${request.static_url('osmtm:static/js/shared.js')}"></script>
<script src="${request.static_url('osmtm:static/bootstrap/dist/js/bootstrap.min.js')}"></script>

<%
from osmtm.models import DBSession, TaskComment
login_url= request.route_path('login', _query=[('came_from', request.url)])
Expand Down
2 changes: 1 addition & 1 deletion osmtm/templates/project.edit.mako
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
</div>
</div>
<script>
var converter = new Showdown.converter();
var converter = new showdown.Converter({extensions: ['youtube']});
var project_id = ${project.id};
<%
from shapely.wkb import loads
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
'pygments',
'gitversion',
'APScheduler==3.0.3',
'python-oembed == 0.2.1',
]

setup(name='osmtm',
Expand Down