initial commit: img, video, include_code tags
This commit is contained in:
52
liquid_tags/Readme.md
Normal file
52
liquid_tags/Readme.md
Normal file
@@ -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']
|
||||
1
liquid_tags/__init__.py
Normal file
1
liquid_tags/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .liquid_tags import *
|
||||
66
liquid_tags/img.py
Normal file
66
liquid_tags/img.py
Normal file
@@ -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
|
||||
|
||||
92
liquid_tags/include_code.py
Normal file
92
liquid_tags/include_code.py
Normal file
@@ -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
|
||||
14
liquid_tags/liquid_tags.py
Normal file
14
liquid_tags/liquid_tags.py
Normal file
@@ -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)
|
||||
76
liquid_tags/mdx_liquid_tags.py
Normal file
76
liquid_tags/mdx_liquid_tags.py
Normal file
@@ -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)
|
||||
72
liquid_tags/video.py
Normal file
72
liquid_tags/video.py
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user