Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial commit: img, video, include_code tags
- Loading branch information
Showing
7 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Liquid-style Tags | ||
*Author: Jake Vanderplas <jakevdp@cs.washington.edu>* | ||
|
||
This plugin allows liquid-style tags to be inserted into markdown within | ||
Pelican documents. Liquid uses tags bounded by ``{% ... %}``, and is used | ||
to extend markdown in other blogging platforms such as octopress. | ||
|
||
This set of extensions does not actually interface with liquid, but allows | ||
users to define their own liquid-style tags which will be inserted into | ||
the markdown preprocessor stream. There are several built-in tags, which | ||
can be added as follows. | ||
|
||
First, in your pelicanconf.py file, add the plugins you want to use: | ||
|
||
PLUGIN_PATH = '/path/to/pelican-plugins' | ||
PLUGINS = ['liquid_tags.img', 'liquid_tags.video', | ||
'liquid_tags.include_code'] | ||
|
||
There are several options available | ||
|
||
## Image Tag | ||
To insert a sized and labeled image in your document, enable the | ||
``liquid_tags.video`` plugin and use the following: | ||
|
||
{% img [class name(s)] path/to/image [width [height]] [title text | "title text" ["alt text"]] %} | ||
|
||
|
||
## Video Tag | ||
To insert flash/HTML5-friendly video into a post, enable the | ||
``liquid_tags.video`` plugin, and add to your document: | ||
|
||
{% video /url/to/video.mp4 [width] [height] [/path/to/poster.png] %} | ||
|
||
The width and height are in pixels, and can be optionally specified. If they | ||
are not, then the original video size will be used. The poster is an image | ||
which is used as a preview of the video. | ||
|
||
To use a video from file, make sure it's in a static directory and put in | ||
the appropriate url. | ||
|
||
## Include Code | ||
To include code from a file in your document with a link to the original | ||
file, enable the ``liquid_tags.include_code`` plugin, and add to your | ||
document: | ||
|
||
{% include_code myscript.py [Title text] %} | ||
|
||
The script must be in the ``code`` subdirectory of your content folder, and | ||
in order for the resulting hyperlink to work, this directory must be listed | ||
under the STATIC_PATHS setting, e.g.: | ||
|
||
STATIC_PATHS = ['images', 'code'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .liquid_tags import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
""" | ||
Image Tag | ||
--------- | ||
This implements a Liquid-style image tag for Pelican, | ||
based on the octopress image tag [1]_ | ||
Syntax | ||
------ | ||
{% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %} | ||
Examples | ||
-------- | ||
{% img /images/ninja.png Ninja Attack! %} | ||
{% img left half http://site.com/images/ninja.png Ninja Attack! %} | ||
{% img left half http://site.com/images/ninja.png 150 150 "Ninja Attack!" "Ninja in attack posture" %} | ||
Output | ||
------ | ||
<img src="/images/ninja.png"> | ||
<img class="left half" src="http://site.com/images/ninja.png" title="Ninja Attack!" alt="Ninja Attack!"> | ||
<img class="left half" src="http://site.com/images/ninja.png" width="150" height="150" title="Ninja Attack!" alt="Ninja in attack posture"> | ||
[1] https://github.com/imathis/octopress/blob/master/plugins/image_tag.rb | ||
""" | ||
import re | ||
from .mdx_liquid_tags import LiquidTags | ||
|
||
SYNTAX = '{% img [class name(s)] [http[s]:/]/path/to/image [width [height]] [title text | "title text" ["alt text"]] %}' | ||
|
||
# Regular expression to match the entire syntax | ||
ReImg = re.compile("""(?P<class>\S.*\s+)?(?P<src>(?:https?:\/\/|\/|\S+\/)\S+)(?:\s+(?P<width>\d+))?(?:\s+(?P<height>\d+))?(?P<title>\s+.+)?""") | ||
|
||
# Regular expression to split the title and alt text | ||
ReTitleAlt = re.compile("""(?:"|')(?P<title>[^"']+)?(?:"|')\s+(?:"|')(?P<alt>[^"']+)?(?:"|')""") | ||
|
||
|
||
@LiquidTags.register('img') | ||
def img(preprocessor, tag, markup): | ||
markup = markup.strip() | ||
attrs = None | ||
|
||
# Parse the markup string | ||
match = ReImg.search(markup) | ||
if match: | ||
attrs = dict([(key, val.strip()) | ||
for (key, val) in match.groupdict().iteritems() if val]) | ||
else: | ||
raise ValueError('Error processing input. ' | ||
'Expected syntax: {0}'.format(SYNTAX)) | ||
|
||
# Check if alt text is present -- if so, split it from title | ||
if 'title' in attrs: | ||
match = ReTitleAlt.search(attrs['title']) | ||
if match: | ||
attrs.update(match.groupdict()) | ||
if not attrs.get('alt'): | ||
attrs['alt'] = attrs['title'] | ||
|
||
# Return the formatted text | ||
return "<img {0}>".format(' '.join('{0}="{1}"'.format(key, val) | ||
for (key, val) in attrs.iteritems())) | ||
|
||
#---------------------------------------------------------------------- | ||
# This import allows image tag to be a Pelican plugin | ||
from liquid_tags import register | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
""" | ||
Include Code Tag | ||
---------------- | ||
This implements a Liquid-style video tag for Pelican, | ||
based on the octopress video tag [1]_ | ||
Syntax | ||
------ | ||
{% include_code path/to/code [Title text] %} | ||
The "path to code" is relative to the code path in | ||
the content directory (TODO: allow this to be set in configs). | ||
Example | ||
------- | ||
{% include_code myscript.py %} | ||
This will import myscript.py from content/downloads/code/myscript.py | ||
and output the contents in a syntax highlighted code block inside a figure, | ||
with a figcaption listing the file name and download link. | ||
The file link will be valid only if the 'code' directory is listed | ||
in the STATIC_PATHS setting, e.g.: | ||
STATIC_PATHS = ['images', 'code'] | ||
[1] https://github.com/imathis/octopress/blob/master/plugins/include_code.rb | ||
""" | ||
import re | ||
import os | ||
from .mdx_liquid_tags import LiquidTags | ||
|
||
|
||
SYNTAX = "{% include_code /path/to/code.py [lang:python] [title] %}" | ||
FORMAT = re.compile(r"""^(?:\s+)?(?P<src>\S+)(?:\s+)?(?:(?:lang:)(?P<lang>\S+))?(?:\s+)?(?P<title>.+)?$""") | ||
|
||
|
||
@LiquidTags.register('include_code') | ||
def include_code(preprocessor, tag, markup): | ||
markup = markup.strip() | ||
|
||
title = None | ||
lang = None | ||
src = None | ||
|
||
match = FORMAT.search(markup) | ||
if match: | ||
argdict = match.groupdict() | ||
title = argdict['title'] | ||
lang = argdict['lang'] | ||
src = argdict['src'] | ||
|
||
if not src: | ||
raise ValueError("Error processing input, " | ||
"expected syntax: {0}".format(SYNTAX)) | ||
|
||
# TODO: make this directory a configurable setting | ||
code_dir = 'code' | ||
code_path = os.path.join('content', code_dir, src) | ||
|
||
if not os.path.exists(code_path): | ||
return "File {0} could not be found".format(code_path) | ||
|
||
code = open(code_path).read() | ||
|
||
if title: | ||
title = "{0} {1}".format(title, os.path.basename(src)) | ||
else: | ||
title = os.path.basename(src) | ||
|
||
url = '/{0}/{1}/{2}'.format('static', code_dir, src) | ||
|
||
open_tag = ("<figure class='code'>\n<figcaption><span>{title}</span> " | ||
"<a href='{url}'>download</a></figcaption>".format(title=title, | ||
url=url)) | ||
close_tag = "</figure>" | ||
|
||
# store HTML tags in the stash. This prevents them from being | ||
# modified by markdown. | ||
open_tag = preprocessor.configs.htmlStash.store(open_tag, safe=True) | ||
close_tag = preprocessor.configs.htmlStash.store(close_tag, safe=True) | ||
|
||
source = (open_tag | ||
+ '\n\n ' + '\n '.join(code.split('\n')) + '\n\n' | ||
+ close_tag + '\n') | ||
|
||
return source | ||
|
||
|
||
#---------------------------------------------------------------------- | ||
# This import allows image tag to be a Pelican plugin | ||
from liquid_tags import register |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from pelican import signals | ||
from mdx_liquid_tags import LiquidTags | ||
from pelican.readers import EXTENSIONS | ||
|
||
def addLiquidTags(gen): | ||
if not gen.settings.get('MD_EXTENSIONS'): | ||
MDReader = EXTENSIONS['markdown'] | ||
gen.settings['MD_EXTENSIONS'] = MDReader.default_extensions | ||
|
||
if LiquidTags not in gen.settings['MD_EXTENSIONS']: | ||
gen.settings['MD_EXTENSIONS'].append(LiquidTags()) | ||
|
||
def register(): | ||
signals.initialized.connect(addLiquidTags) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
""" | ||
Markdown Extension for Liquid-style Tags | ||
---------------------------------------- | ||
A markdown extension to allow user-defined tags of the form:: | ||
{% tag arg1 arg2 ... argn %} | ||
Where "tag" is associated with some user-defined extension. | ||
These result in a preprocess step within markdown that produces | ||
either markdown or html. | ||
""" | ||
import markdown | ||
import itertools | ||
import re | ||
import os | ||
from functools import wraps | ||
|
||
# Define some regular expressions | ||
LIQUID_TAG = re.compile(r'\{%.*?%\}') | ||
EXTRACT_TAG = re.compile(r'(?:\s*)(\S+)(?:\s*)') | ||
|
||
|
||
class _LiquidTagsPreprocessor(markdown.preprocessors.Preprocessor): | ||
_tags = {} | ||
def __init__(self, configs): | ||
self.configs = configs | ||
|
||
def run(self, lines): | ||
page = '\n'.join(lines) | ||
liquid_tags = LIQUID_TAG.findall(page) | ||
|
||
for i, markup in enumerate(liquid_tags): | ||
# remove {% %} | ||
markup = markup[2:-2] | ||
tag = EXTRACT_TAG.match(markup).groups()[0] | ||
markup = EXTRACT_TAG.sub('', markup, 1) | ||
if tag in self._tags: | ||
liquid_tags[i] = self._tags[tag](self, tag, markup) | ||
|
||
# add an empty string to liquid_tags so that chaining works | ||
liquid_tags.append('') | ||
|
||
# reconstruct string | ||
page = ''.join(itertools.chain(*zip(LIQUID_TAG.split(page), | ||
liquid_tags))) | ||
|
||
# resplit the lines | ||
return page.split("\n") | ||
|
||
|
||
class LiquidTags(markdown.Extension): | ||
"""Wrapper for MDPreprocessor""" | ||
@classmethod | ||
def register(cls, tag): | ||
"""Decorator to register a new include tag""" | ||
def dec(func): | ||
if tag in _LiquidTagsPreprocessor._tags: | ||
warnings.warn("Enhanced Markdown: overriding tag '%s'" % tag) | ||
_LiquidTagsPreprocessor._tags[tag] = func | ||
return func | ||
return dec | ||
|
||
def extendMarkdown(self, md, md_globals): | ||
self.htmlStash = md.htmlStash | ||
md.registerExtension(self) | ||
# for the include_code preprocessor, we need to re-run the | ||
# fenced code block preprocessor after substituting the code. | ||
# Because the fenced code processor is run before, {% %} tags | ||
# within equations will not be parsed as an include. | ||
md.preprocessors.add('mdincludes', | ||
_LiquidTagsPreprocessor(self), ">html_block") | ||
|
||
|
||
def makeExtension(configs=None): | ||
"""Wrapper for a MarkDown extension""" | ||
return LiquidTags(configs=configs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
""" | ||
Video Tag | ||
--------- | ||
This implements a Liquid-style video tag for Pelican, | ||
based on the octopress video tag [1]_ | ||
Syntax | ||
------ | ||
{% video url/to/video [width height] [url/to/poster] %} | ||
Example | ||
------- | ||
{% video http://site.com/video.mp4 720 480 http://site.com/poster-frame.jpg %} | ||
Output | ||
------ | ||
<video width='720' height='480' preload='none' controls poster='http://site.com/poster-frame.jpg'> | ||
<source src='http://site.com/video.mp4' type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'/> | ||
</video> | ||
[1] https://github.com/imathis/octopress/blob/master/plugins/video_tag.rb | ||
""" | ||
import os | ||
import re | ||
from .mdx_liquid_tags import LiquidTags | ||
|
||
SYNTAX = "{% video url/to/video [url/to/video] [url/to/video] [width height] [url/to/poster] %}" | ||
|
||
VIDEO = re.compile(r'(/\S+|https?:\S+)(\s+(/\S+|https?:\S+))?(\s+(/\S+|https?:\S+))?(\s+(\d+)\s(\d+))?(\s+(/\S+|https?:\S+))?') | ||
|
||
VID_TYPEDICT = {'.mp4':"type='video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'", | ||
'.ogv':"type='video/ogg; codecs=theora, vorbis'", | ||
'.webm':"type='video/webm; codecs=vp8, vorbis'"} | ||
|
||
|
||
@LiquidTags.register('video') | ||
def video(preprocessor, tag, markup): | ||
markup = markup.strip() | ||
|
||
videos = [] | ||
width = None | ||
height = None | ||
poster = None | ||
|
||
match = VIDEO.search(markup) | ||
if match: | ||
groups = match.groups() | ||
videos = [g for g in groups[0:6:2] if g] | ||
width = groups[6] | ||
height = groups[7] | ||
poster = groups[9] | ||
|
||
if any(videos): | ||
video_out = "<video width='{width}' height='{height}' preload='none' controls poster='{poster}'>".format(width=width, height=height, poster=poster) | ||
for vid in videos: | ||
base, ext = os.path.splitext(vid) | ||
if ext not in VID_TYPEDICT: | ||
raise ValueError("Unrecognized video extension: " | ||
"{0}".format(ext)) | ||
video_out += ("<source src='{0}' " | ||
"{1}>".format(vid, VID_TYPEDICT[ext])) | ||
video_out += "</video>" | ||
else: | ||
raise ValueError("Error processing input, " | ||
"expected syntax: {0}".format(SYNTAX)) | ||
|
||
return video_out | ||
|
||
|
||
#---------------------------------------------------------------------- | ||
# This import allows image tag to be a Pelican plugin | ||
from liquid_tags import register |