better markmin, thanks Vladyslav

This commit is contained in:
mdipierro
2012-07-11 23:13:59 -05:00
parent cdda1ee79e
commit 4e8f57cbd9
4 changed files with 419 additions and 179 deletions
+353 -135
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python
# created my Massimo Di Pierro
#!/usr/bin/env python
# created by Massimo Di Pierro
# improved by Vladyslav Kozlovskyy
# license MIT/BSD/GPL
import re
import cgi
@@ -20,7 +21,7 @@ MathJax.Hub.Config({
</script>
"""
__all__ = ['render', 'markmin2html']
__all__ = ['render', 'markmin2html', 'markmin_escape']
__doc__ = """
# Markmin markup language
@@ -44,7 +45,7 @@ print markmin2pdf(m)
## Why?
We wanted a markup language with the following requirements:
- less than 100 lines of functional code
- less than 200 lines of functional code
- easy to read
- secure
- support table, ul, ol, code
@@ -75,33 +76,38 @@ markmin2html.py and markmin2latex.py are single files and have no web2py depende
### Bold, italic, code and links
--------------------------------------------------
**SOURCE** | **OUTPUT**
``# title`` | **title**
``## section`` | **section**
``### subsection`` | **subsection**
``**bold**`` | **bold**
``''italic''`` | ''italic''
``!`!`verbatim`!`!`` | ``verbatim``
``http://google.com`` | http://google.com
``[[click me #myanchor]]`` | [[click me #myanchor]]
---------------------------------------------------
------------------------------------------------------------------------------
**SOURCE** | **OUTPUT**
``# title`` | **title**
``## section`` | **section**
``### subsection`` | **subsection**
``**bold**`` | **bold**
``''italic''`` | ''italic''
``~~strikeout~~`` | ~~strikeout~~
``!`!`verbatim`!`!`` | ``verbatim``
``\`\`color with **bold**\`\`:red`` | ``color with **bold**``:red
``\`\`many colors\`\`:color[blue:#ffff00]`` | ``many colors``:color[blue:#ffff00]
``http://google.com`` | http://google.com
``[[**click** me #myanchor]]`` | [[**click** me #myanchor]]
``[[click me [extra info] #myanchor popup]]`` | [[click me [extra info] #myanchor popup]]
-------------------------------------------------------------------------------
### More on links
The format is always ``[[title link]]``. Notice you can nest bold, italic and code inside the link title.
The format is always ``[[title link]]`` or ``[[title [extra] link]]``. Notice you can nest bold, italic, strikeout and code inside the link ``title``.
### Anchors [[myanchor]]
You can place an anchor anywhere in the text using the syntax ``[[name]]`` where ''name'' is the name of the anchor.
You can then link the anchor with [[link #myanchor]], i.e. ``[[link #myanchor]]``.
You can then link the anchor with [[link #myanchor]], i.e. ``[[link #myanchor]]`` or [[link with an extra info [extra info] #myanchor]], i.e.
``[[link with an extra info [extra info] #myanchor]]``.
### Images
[[some image http://www.web2py.com/examples/static/web2py_logo.png right 200px]]
[[alt-string for the image [the image title] http://www.web2py.com/examples/static/web2py_logo.png right 200px]]
This paragraph has an image aligned to the right with a width of 200px. Its is placed using the code
``[[some image http://www.web2py.com/examples/static/web2py_logo.png right 200px]]``.
``[[alt-string for the image [the image title] http://www.web2py.com/examples/static/web2py_logo.png right 200px]]``.
### Unordered Lists
@@ -169,6 +175,10 @@ def test():
``:python
Optionally a ` inside a ``!`!`...`!`!`` block can be inserted escaped with !`!.
**NOTE:** You can escape markmin constructions (\\'\\',\`\`,\*\*,\~\~,\[,\{,\]\},\$,\@) with '\\\\' character:
so \\\\`\\\\` can replace !`!`! escape string
The ``:python`` after the markup is also optional. If present, by default, it is used to set the class of the <code> block.
The behavior can be overridden by passing an argument ``extra`` to the ``render`` function. For example:
@@ -183,23 +193,26 @@ generates
(the ``!`!`...`!`!:custom`` block is rendered by the ``custom=lambda`` function passed to ``render``).
### Html5 support
Markmin also supports the <video> and <audio> html5 tags using the notation:
``
[[title link video]]
[[title link audio]]
[[message link video]]
[[message link audio]]
[[message [title] link video]]
[[message [title] link audio]]
``
where ``message`` will be shown in brousers without HTML5 video/audio tags support.
### Latex and other extensions
Formulas can be embedded into HTML with ``$````$``formula``$````$``.
Formulas can be embedded into HTML with ''\$\$``formula``\$\$''.
You can use Google charts to render the formula:
``
LATEX = '<img src="http://chart.apis.google.com/chart?cht=tx&chl=%s" />'
markmin2html(text,{'latex':lambda code: LATEX % code.replace('"','\"')})
markmin2html(text,{'latex':lambda code: LATEX % code.replace('"','\\\\"')})
``
### Code with syntax highlighting
@@ -211,16 +224,27 @@ extra={'code_cpp':lambda text: CODE(text,language='cpp').xml(),
'code_java':lambda text: CODE(text,language='java').xml(),
'code_python':lambda text: CODE(text,language='python').xml(),
'code_html':lambda text: CODE(text,language='html').xml()}
``
or simple:
``
extra={'code':lambda text,lang='': CODE(text,language=lang).xml()}
``
``
markmin2html(text,extra=extra)
``
Code can now be marked up as in this example:
``
!`!`
<html><body>example</body></html>
!`!`:code_html
``
OR
``
!`!`
<html><body>example</body></html>
!`!`:code[html]
``
### Citations and References
@@ -249,17 +273,26 @@ As shown in Ref.!`!`mdipierro`!`!:cite
``<ul/>``, ``<ol/>``, ``<code/>``, ``<table/>``, ``<blockquote/>``, ``<h1/>``, ..., ``<h6/>`` do not have ``<p>...</p>`` around them.
"""
html_colors=['aqua', 'black', 'blue', 'fuchsia', 'gray', 'green',
'lime', 'maroon', 'navy', 'olive', 'purple', 'red',
'silver', 'teal', 'white', 'yellow']
META = 'META'
LINK = 'LINK'
DISABLED_META = META[::-1]
LATEX = '<img src="http://chart.apis.google.com/chart?cht=tx&chl=%s" />'
regex_URL=re.compile(r'(?P<b>(?<!\\)(?:\\\\)*)@\{(?P<f>\w+)/(?P<args>.+?)\}')
regex_env=re.compile(r'(?P<b>(?<!\\)(?:\\\\)*)@\{(?P<a>\w+?)\}')
regex_expand_meta = re.compile('('+META+'|'+DISABLED_META+')')
regex_newlines = re.compile(r'(\n\r)|(\r\n)')
regex_dd=re.compile(r'\$\$(?P<latex>.*?)\$\$')
regex_code = re.compile(r'('+META+r')|(``(?P<t>.*?)``(:(?P<c>\w+))?)',re.S)
regex_code = re.compile('('+META+'|'+DISABLED_META+r')|((?P<b>(?<!\\)(?:\\\\)*)``(?P<t>.*?(?<!\\)(?:\\\\)*)``(?:(?<!\\):(?P<c>\w+)(?:(?<!\\)\[(?P<p>\S+?)(?<!\\)\])?)?)',re.S)
regex_maps = [
(re.compile(r'[ \t\r]+\n'),'\n'),
(re.compile(r'[ \t\r]+\n'),'\n'),
(re.compile(r'\*\*(?P<t>[^\s*]+( +[^\s*]+)*)\*\*'),'<strong>\g<t></strong>'),
(re.compile("''(?P<t>[^\s']+( +[^\s']+)*)''"),'<em>\g<t></em>'),
(re.compile(r'(?P<b1>(?<!\\)(?:\\\\)*)\*\*(?P<t>[^\s*]+( +[^\s*]+)*)(?P<b2>(?<!\\)(?:\\\\)*)\*\*'),'\g<b1><strong>\g<t>\g<b2></strong>'),
(re.compile(r'(?P<b1>(?<!\\)(?:\\\\)*)~~(?P<t>[^\s*]+( +[^\s*]+)*)(?P<b2>(?<!\\)(?:\\\\)*)~~'),'\g<b1><del>\g<t>\g<b2></del>'),
(re.compile(r"(?P<b1>(?<!\\)(?:\\\\)*)''(?P<t>[^\s']+(?: +[^\s']+)*)(?P<b2>(?<!\\)(?:\\\\)*)''"),'\g<b1><em>\g<t>\g<b2></em>'),
(re.compile(r'^#{6} (?P<t>[^\n]+)',re.M),'\n\n<<h6>\g<t></h6>\n'),
(re.compile(r'^#{5} (?P<t>[^\n]+)',re.M),'\n\n<<h5>\g<t></h5>\n'),
(re.compile(r'^#{4} (?P<t>[^\n]+)',re.M),'\n\n<<h4>\g<t></h4>\n'),
@@ -273,25 +306,29 @@ regex_maps = [
(re.compile(r'<<'),'\n\n<<'),
(re.compile(r'\n\s+\n'),'\n\n')]
regex_table = re.compile(r'^\-{4,}\n(?P<t>.*?)\n\-{4,}(:(?P<c>\w+))?\n',re.M|re.S)
regex_anchor = re.compile(r'\[\[(?P<t>\S+)\]\]')
regex_image_center_width = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) +center +(?P<w>\d+px)\]\]')
regex_image_width = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) +(?P<p>left|right) +(?P<w>\d+px)\]\]')
regex_image_center = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) +center\]\]')
regex_image = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) +(?P<p>left|right)\]\]')
regex_video = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) +video\]\]')
regex_audio = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) +audio\]\]')
regex_link = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+)\]\]')
regex_link_popup = re.compile(r'\[\[(?P<t>[^\]]*?) +(?P<k>\S+) popup\]\]')
regex_link_no_anchor = re.compile(r'\[\[ +(?P<k>\S+)\]\]')
regex_qr = re.compile(r'(?<!["\w>/=])qr:(?P<k>\w+://[\w.\-+?&%/:]+)',re.M)
regex_embed = re.compile(r'(?<!["\w>/=])embed:(?P<k>\w+://[\w.\-+?&%/:]+)', re.M)
regex_iframe = re.compile(r'(?<!["\w>/=])iframe:(?P<k>\w+://[\w.\-+?&%/:]+)', re.M)
regex_auto_image = re.compile(r'(?<!["\w>/=])(?P<k>\w+://\S+\.(jpeg|JPEG|jpg|JPG|gif|GIF|png|PNG)(\?\S+)?)',re.M)
regex_auto_video = re.compile(r'(?<!["\w>/=])(?P<k>\w+://\S+\.(mp4|MP4|mpeg|MPEG|mov|MOV)(\?\S+)?)',re.M)
regex_auto_audio = re.compile(r'(?<!["\w>/=])(?P<k>\w+://\S+\.(mp3|MP3|wav|WAV)(\?\S+)?)',re.M)
regex_auto = re.compile(r'(?<!["\w>/=])(?P<k>\w+://\S+)',re.M)
regex_qr = re.compile(r'(?<!["\w>/=])qr:(?P<k>\w+://[\w\d\-+?&%/:.]+)',re.M)
regex_embed = re.compile(r'(?<!["\w>/=])embed:(?P<k>\w+://[\w\d\-+_=?%&/:.]+)', re.M)
regex_iframe = re.compile(r'(?<!["\w>/=])iframe:(?P<k>\w+://[\w\d\-+=?%&/:.]+)', re.M)
regex_auto_image = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[\w\d\-+_=%&/:.]+\.(jpeg|JPEG|jpg|JPG|gif|GIF|png|PNG)(\?[\w\d/\-+_=%&:.]+)?)',re.M)
regex_auto_video = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[\w\d\-+_=%&/:.]+\.(mp4|MP4|mpeg|MPEG|mov|MOV)(\?[\w\d/\-+_=%&:.]+)?)',re.M)
regex_auto_audio = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[\w\d\-+_=%&/:.]+\.(mp3|MP3|wav|WAV)(\?[\w\d/\-+_=%&:.]+)?)',re.M)
regex_auto = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[\w\d\-+_=?%&/:.]+)',re.M)
def render(text,extra={},allowed={},sep='p',URL=None,environment=None,latex='google'):
regex_link=re.compile(r'('+LINK+r')|(?P<b>(?<!\\)(?:\\\\)*)\[\[(?P<s>.*?)(?<!\\)\]\]')
regex_link_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+(?<!\\)\[(?P<a>.+?(?<!\\)(?:\\\\)*)\])?(?:\s+(?P<k>\S+))?(?:\s+(?P<p>popup))?\s*$')
regex_media_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+(?<!\\)\[(?P<a>.+?(?<!\\)(?:\\\\)*)\])?(?:\s+(?P<k>\S+))?\s+(?P<p>img|IMG|left|right|center|video|audio)(?:\s+(?P<w>\d+px))?\s*$')
regex_backslash = re.compile(r"(\\+)(['`:*~\\[\]{}@\$])")
regex_markmin_escape = re.compile(r"(\\*)(['`:*~\\[\]{}@\$])")
def markmin_escape(text):
""" insert \\ before markmin control characters: '`:*~[]{}@$ """
return regex_markmin_escape.sub(lambda m: '\\'+m.group(0).replace('\\','\\\\'), text)
def remove_backslashes(text):
return regex_backslash.sub(lambda m: m.group(0)[m.group(0).find('\\')+1:].replace('\\\\','\\') , text)
def render(text,extra={},allowed={},sep='p',URL=None,environment=None,latex='google',auto=True):
"""
Arguments:
- text is the text to be processed
@@ -301,7 +338,8 @@ def render(text,extra={},allowed={},sep='p',URL=None,environment=None,latex='goo
allowed = dict(code=('python','cpp','java'))
- sep can be 'p' to separate text in <p>...</p>
or can be 'br' to separate text using <br />
- auto is a True/False value (default is True) -
enables auto links processing for iframe,embed,qr,url,image,video,audio
>>> render('this is\\n# a section\\nparagraph')
'<p>this is</p><h1>a section</h1><p>paragraph</p>'
@@ -338,67 +376,211 @@ def render(text,extra={},allowed={},sep='p',URL=None,environment=None,latex='goo
'<p><a href="http://example.com">this is a link</a></p>'
>>> render('[[this is an image http://example.com left]]')
'<p><img src="http://example.com" alt="this is an image" align="left" /></p>'
>>> render('[[this is an image http://example.com left 200px]]')
'<p><img src="http://example.com" alt="this is an image" align="left" width="200px" /></p>'
'<p><img src="http://example.com" alt="this is an image" style="float:left" /></p>'
>>> render('[[this is an image http://example.com video]]')
'<p><video controls="controls"><source src="http://example.com" /></video></p>'
>>> render('[[this is an image http://example.com audio]]')
'<p><audio controls="controls"><source src="http://example.com" /></audio></p>'
>>> render('[[this is an image http://example.com left 200px]]')
'<p><img src="http://example.com" alt="this is an image" style="float:left" width="200px" /></p>'
>>> render("[[Your browser doesn't support <video> HTML5 tag http://example.com video]]")
'<p><video controls="controls"><source src="http://example.com" />Your browser doesn\\'t support &lt;video&gt; HTML5 tag</video></p>'
>>> render("[[Your browser doesn't support <audio> HTML5 tag http://example.com audio]]")
'<p><audio controls="controls"><source src="http://example.com" />Your browser doesn\\'t support &lt;audio&gt; HTML5 tag</audio></p>'
>>> render('[[this is a **link** http://example.com]]')
'<p><a href="http://example.com">this is a <strong>link</strong></a></p>'
>>> render("``aaa``:custom",extra=dict(custom=lambda text: 'x'+text+'x'))
>>> render("``aaa``:custom", extra=dict(custom=lambda text: 'x'+text+'x'))
'xaaax'
>>> print render(r"$$\int_a^b sin(x)dx$$")
<img src="http://chart.apis.google.com/chart?cht=tx&chl=\\int_a^b sin(x)dx" />
>>> markmin2html(r"use backslash: \[\[[[mess\[[ag\]]e link]]\]]")
'<p>use backslash: [[<a href="link">mess[[ag]]e</a>]]</p>'
>>> markmin2html("backslash instead of exclamation sign: \``probe``")
'<p>backslash instead of exclamation sign: ``probe``</p>'
>>> render(r"simple image: [[\[[this is an image\]] http://example.com IMG]]!!!")
'<p>simple image: <img src="http://example.com" alt="[[this is an image]]" />!!!</p>'
>>> render(r"simple link no anchor with popup: [[ http://example.com popup]]")
'<p>simple link no anchor with popup: <a href="http://example.com" target="_blank">http://example.com</a></p>'
>>> render("auto-url: http://example.com")
'<p>auto-url: <a href="http://example.com">http://example.com</a></p>'
>>> render("auto-image: (http://example.com/image.jpeg)")
'<p>auto-image: (<img src="http://example.com/image.jpeg" controls />)</p>'
>>> render("title1: [[test message [simple \[test\] title] http://example.com ]] test")
'<p>title1: <a href="http://example.com" title="simple [test] title">test message</a> test</p>'
>>> render("title2: \[\[[[test message [simple title] http://example.com popup]]\]]")
'<p>title2: [[<a href="http://example.com" title="simple title" target="_blank">test message</a>]]</p>'
>>> render("title3: [[ [link w/o anchor but with title] http://www.example.com ]]")
'<p>title3: <a href="http://www.example.com" title="link w/o anchor but with title">http://www.example.com</a></p>'
>>> render("title4: [[ [simple title] http://www.example.com popup]]")
'<p>title4: <a href="http://www.example.com" title="simple title" target="_blank">http://www.example.com</a></p>'
>>> render("title5: [[test message [simple title] http://example.com IMG]]")
'<p>title5: <img src="http://example.com" alt="test message" title="simple title" /></p>'
>>> render("title6: [[[test message w/o title] http://example.com IMG]]")
'<p>title6: <img src="http://example.com" alt="[test message w/o title]" /></p>'
>>> render("title7: [[[this is not a title] [this is a title] http://example.com IMG]]")
'<p>title7: <img src="http://example.com" alt="[this is not a title]" title="this is a title" /></p>'
>>> render("title8: [[test message [title] http://example.com center]]")
'<p>title8: <p style="text-align:center"><img src="http://example.com" alt="test message" title="title" /></p></p>'
>>> render("title9: [[test message [title] http://example.com left]]")
'<p>title9: <img src="http://example.com" alt="test message" title="title" style="float:left" /></p>'
>>> render("title10: [[test message [title] http://example.com right 100px]]")
'<p>title10: <img src="http://example.com" alt="test message" title="title" style="float:right" width="100px" /></p>'
>>> render("title11: [[test message [title] http://example.com center 200px]]")
'<p>title11: <p style="text-align:center"><img src="http://example.com" alt="test message" title="title" width="200px" /></p></p>'
>>> render(r"\\[[probe]]")
'<p>[[probe]]</p>'
>>> render(r"\\\\[[probe]]")
'<p>\\\\<span id="probe"></span></p>'
>>> render(r"\\\\\\[[probe]]")
'<p>\\\\[[probe]]</p>'
>>> render(r"\\\\\\\\[[probe]]")
'<p>\\\\\\\\<span id="probe"></span></p>'
>>> render(r"\\\\\\\\\[[probe]]")
'<p>\\\\\\\\[[probe]]</p>'
>>> render(r"\\\\\\\\\\\[[probe]]")
'<p>\\\\\\\\\\\\<span id="probe"></span></p>'
>>> render("``[[ [\\[[probe\]\\]] URL\\[x\\]]]``:red[dummy_params]")
'<span style="color: red"><a href="URL[x]" title="[[probe]]">URL[x]</a></span>'
>>> render("the \\**text**")
'<p>the **text**</p>'
>>> render("the \\``text``")
'<p>the ``text``</p>'
>>> render("the \\\\''text''")
"<p>the ''text''</p>"
>>> render("the [[link [**with** ``<b>title</b>``:red] http://www.example.com]]")
'<p>the <a href="http://www.example.com" title="**with** ``&lt;b&gt;title&lt;/b&gt;``:red">link</a></p>'
>>> render("the [[link \\[**without** ``<b>title</b>``:red\\] http://www.example.com]]")
'<p>the <a href="http://www.example.com">link [<strong>without</strong> <span style="color: red">&lt;b&gt;title&lt;/b&gt;</span>]</a></p>'
>>> render("aaa-META-``code``:text-LINK-[[link http://www.example.com]]-LINK-[[image http://www.picture.com img]]-end")
'<p>aaa-META-<code class="text">code</code>-LINK-<a href="http://www.example.com">link</a>-LINK-<img src="http://www.picture.com" alt="image" />-end</p>'
>>> render("[[<a>test</a> [<a>test2</a>] <a>text3</a>]]")
'<p><a href="&lt;a&gt;text3&lt;/a&gt;" title="&lt;a&gt;test2&lt;/a&gt;">&lt;a&gt;test&lt;/a&gt;</a></p>'
>>> render("[[<a>test</a> [<a>test2</a>] <a>text3</a> IMG]]")
'<p><img src="&lt;a&gt;text3&lt;/a&gt;" alt="&lt;a&gt;test&lt;/a&gt;" title="&lt;a&gt;test2&lt;/a&gt;" /></p>'
>>> render("**bold** ''italic'' ~~strikeout~~")
'<p><strong>bold</strong> <em>italic</em> <del>strikeout</del></p>'
>>> render("this is ``a red on yellow text``:c[#FF0000:#FFFF00]")
'<p>this is <span style="color: #FF0000;background-color: #FFFF00;">a red on yellow text</span></p>'
>>> render("this is ``a text with yellow background``:c[:yellow]")
'<p>this is <span style="background-color: yellow;">a text with yellow background</span></p>'
>>> render("this is ``a colored text (RoyalBlue)``:color[rgb(65,105,225)]")
'<p>this is <span style="color: rgb(65,105,225);">a colored text (RoyalBlue)</span></p>'
>>> render("this is ``a green text``:color[green:]")
'<p>this is <span style="color: green;">a green text</span></p>'
>>> render("**@{probe}**", environment=dict(probe="this is a test"))
'<p><strong>this is a test</strong></p>'
"""
text = str(text or '')
if environment:
def u2(match,environment=environment):
a = match.group('a')
return str(environment[a])
text = re.compile(r'@\{(?P<a>\w+?)\}').sub(u2,text)
if not URL is None:
# this is experimental @{function/args}
def u2(match, environment=environment):
b,a = match.group('b','a')
return b + str(environment.get(a, match.group(0)))
text = regex_env.sub(u2,text)
if URL is not None:
# this is experimental @{controller/index/args}
# turns into a digitally signed URL
def u1(match,URL=URL):
f,args = match.group('f'), match.group('args')
return URL(f,args=args.split('/'),scheme=True,host=True)
text = re.compile(
'@\{(?P<f>\w+)/(?P<args>.+?)\}'
).sub(u1,text)
b,f,args = match.group('b','f','args')
return b + URL(f,args=args.split('/'),scheme=True,host=True)
text = regex_URL.sub(u1,text)
if latex == 'google':
text = regex_dd.sub('``\g<latex>``:latex ', text)
text = regex_newlines.sub('\n',text)
#############################################################
# replace all blocks marked with ``...``:class with META
# store them into segments they will be treated as code
#############################################################
segments, i = [], 0
if latex == 'google':
text = regex_dd.sub('``\g<latex>``:latex ',text)
text = regex_newlines.sub('\n',text)
while True:
item = regex_code.search(text,i)
if not item: break
if item.group()==META:
segments.append((None,None))
text = text[:item.start()]+META+text[item.end():]
segments = []
def mark_code(m):
if m.group() in ( META, DISABLED_META ):
segments.append((None, None, None, m.group(0)))
return m.group()
else:
c = item.group('c') or ''
c = m.group('c') or ''
p = m.group('p') or ''
b = m.group('b') or ''
if 'code' in allowed and not c in allowed['code']: c = ''
code = item.group('t').replace('!`!','`')
segments.append((code,c))
text = text[:item.start()]+META+text[item.end():]
i=item.start()+3
code = m.group('t').replace('!`!','`')
segments.append((code, c, p, m.group(0)))
return b + META
text = regex_code.sub(mark_code, text)
#############################################################
# do h1,h2,h3,h4,h5,h6,b,i,ol,ul and normalize spaces
# replace all blocks marked with [[...]] with LINK
# store them into links|medias they will be treated as link
#############################################################
links = []
def mark_link(m):
if m.group() == LINK:
links.append(None)
b = ''
else:
s = m.group('s') or ''
b = m.group('b') or ''
links.append(s)
return b + LINK
text = regex_link.sub(mark_link, text)
#############################################################
# normalize spaces
#############################################################
text = '\n'.join(t.strip() for t in text.split('\n'))
text = cgi.escape(text)
if auto:
text = regex_iframe.sub('<iframe src="\g<k>" frameborder="0" allowfullscreen></iframe>',text)
text = regex_embed.sub('<a href="\g<k>" class="embed">\g<k></a>',text)
text = regex_qr.sub('<img width="80px" src="http://qrcode.kaywa.com/img.php?s=8&amp;d=\g<k>" alt="qr code" />',text)
text = regex_auto_image.sub('<img src="\g<k>" controls />', text)
text = regex_auto_video.sub('<video src="\g<k>" controls></video>', text)
text = regex_auto_audio.sub('<audio src="\g<k>" controls></audio>', text)
text = regex_auto.sub('<a href="\g<k>">\g<k></a>', text)
#############################################################
# do h1,h2,h3,h4,h5,h6,b,i,ol,ul
#############################################################
for regex, sub in regex_maps:
text = regex.sub(sub,text)
@@ -417,28 +599,6 @@ def render(text,extra={},allowed={},sep='p',URL=None,environment=None,latex='goo
else:
text = text[:item.start()] + '<<blockquote class="%s">'%c + content + '</blockquote>\n' + text[item.end():]
#############################################################
# deal with images, videos, audios and links
#############################################################
text = regex_anchor.sub('<span id="\g<t>"><span>', text)
text = regex_image_center_width.sub('<p style="text-align:center"><img src="\g<k>" alt="\g<t>" width="\g<w>" /></p>', text)
text = regex_image_width.sub('<img src="\g<k>" alt="\g<t>" style="float:\g<p>" width="\g<w>" />', text)
text = regex_image_center.sub('<p style="text-align:center"><img src="\g<k>" alt="\g<t>" /></p>', text)
text = regex_image.sub('<img src="\g<k>" alt="\g<t>" style="float:\g<p>" />', text)
text = regex_video.sub('<video controls="controls"><source src="\g<k>" /></video>', text)
text = regex_audio.sub('<audio controls="controls"><source src="\g<k>" /></audio>', text)
text = regex_link_popup.sub('<a href="\g<k>" target="_blank">\g<t></a>', text)
text = regex_link_no_anchor.sub('<a href="\g<k>">\g<k></a>', text)
text = regex_link.sub('<a href="\g<k>">\g<t></a>', text)
text = regex_qr.sub('<img width="80px" src="http://qrcode.kaywa.com/img.php?s=8&amp;d=\g<k>" alt="qr code" />',text)
text = regex_iframe.sub('<iframe src="\g<k>" frameborder="0" allowfullscreen></iframe>',text)
text = regex_embed.sub('<a href="\g<k>" class="embed">\g<k></a>',text)
text = regex_auto_image.sub('<img src="\g<k>" controls />', text)
text = regex_auto_video.sub('<video src="\g<k>" controls></video>', text)
text = regex_auto_audio.sub('<audio src="\g<k>" controls></audio>', text)
text = regex_auto.sub('<a href="\g<k>">\g<k></a>', text)
#############################################################
# deal with paragraphs (trick <<ul, <<ol, <<table, <<h1, etc)
# the << indicates that there should NOT be a new paragraph
@@ -457,46 +617,104 @@ def render(text,extra={},allowed={},sep='p',URL=None,environment=None,latex='goo
#############################################################
text=text.replace('<<','<')
#############################################################
# deal with images, videos, audios and links
#############################################################
def sub_media(m):
d=m.groupdict()
if not d['k']:
return m.group(0)
d['k'] = cgi.escape(d['k'])
d['t'] = d['t'] or ''
d['width'] = ' width="%s"'%d['w'] if d['w'] else ''
d['title'] = ' title="%s"'%cgi.escape(d['a']).replace(META, DISABLED_META) if d['a'] else ''
d['style'] = d['p_begin'] = d['p_end'] = ''
if d['p'] == 'center':
d['p_begin'] = '<p style="text-align:center">'
d['p_end'] = '</p>'
elif d['p'] in ('left','right'):
d['style'] = ' style="float:%s"'%d['p']
if d['p'] in ('video','audio'):
d['t']=render(d['t'],{},{},'',URL,environment,latex,auto)
return '<%(p)s controls="controls"%(title)s%(width)s><source src="%(k)s" />%(t)s</%(p)s>'%d
d['alt'] = ' alt="%s"'%cgi.escape(d['t']).replace(META, DISABLED_META) if d['t'] else ''
return '%(p_begin)s<img src="%(k)s"%(alt)s%(title)s%(style)s%(width)s />%(p_end)s'%d
def sub_link(m):
d=m.groupdict()
if not d['k'] and not d['t']:
return m.group(0)
d['t'] = d['t'] or ''
d['a'] = cgi.escape(d['a']) if d['a'] else ''
if d['k']:
d['k'] = cgi.escape(d['k'])
d['title'] = ' title="%s"' % d['a'].replace(META, DISABLED_META) if d['a'] else ''
d['target'] = ' target="_blank"' if d['p'] == 'popup' else ''
d['t'] = render(d['t'],{},{},'',URL,environment,latex,auto) if d['t'] else d['k']
return '<a href="%(k)s"%(title)s%(target)s>%(t)s</a>'%d
d['t'] = cgi.escape(d['t'])
return '<span id="%(t)s">%(a)s</span>'%d
parts = text.split(LINK)
text = parts[0]
for i,s in enumerate(links):
if s==None:
html = LINK
else:
html = regex_media_level2.sub(sub_media, s)
if html == s:
html = regex_link_level2.sub(sub_link, html)
if html == s:
# return unprocessed string as a signal of an error
html = '[[%s]]'%s
text = text+html+parts[i+1]
#############################################################
# process all code text
#############################################################
parts = text.split(META)
text = parts[0]
for i,(code,b) in enumerate(segments):
if code==None:
html = META
else:
if b in extra:
if code[:1]=='\n': code=code[1:]
if code[-1:]=='\n': code=code[:-1]
html = extra[b](code)
elif b=='cite':
html = '['+','.join('<a href="#%s" class="%s">%s</a>' \
% (d,b,d) \
for d in cgi.escape(code).split(','))+']'
elif b=='latex':
html = LATEX % code.replace('"','\"').replace('\n',' ')
elif code[:1]=='\n' or code[-1:]=='\n':
if code[:1]=='\n': code=code[1:]
if code[-1:]=='\n': code=code[:-1]
html = '<pre><code class="%s">%s</code></pre>' % (b,cgi.escape(code))
def expand_meta(m):
code,b,p,s = segments.pop(0)
if code==None or m.group() == DISABLED_META:
return cgi.escape(s)
if b in extra:
if code[:1]=='\n': code=code[1:]
if code[-1:]=='\n': code=code[:-1]
if p:
return extra[b](code,p)
else:
if code[:1]=='\n': code=code[1:]
if code[-1:]=='\n': code=code[:-1]
html = '<code class="%s">%s</code>' % (b,cgi.escape(code))
text = text+html+parts[i+1]
return extra[b](code)
elif b=='cite':
return '['+','.join('<a href="#%s" class="%s">%s</a>' \
% (d,b,d) \
for d in cgi.escape(code).split(','))+']'
elif b=='latex':
return LATEX % code.replace('"','\"').replace('\n',' ')
elif b in html_colors:
return '<span style="color: %s">%s</span>' \
% (b, render(code,{},{},'',URL,environment,latex,auto))
elif b in ('c', 'color') and p:
c=p.split(':')
fg='color: %s;' % c[0] if c[0] else ''
bg='background-color: %s;' % c[1] if len(c)>1 and c[1] else ''
return '<span style="%s%s">%s</span>' \
% (fg, bg, render(code,{},{},'',URL,environment,latex,auto))
elif code[:1]=='\n' and code[-1:]=='\n':
return '<pre><code class="%s">%s</code></pre>' % (b,cgi.escape(code[1:-1]))
return '<code class="%s">%s</code>' % (b,cgi.escape(code[ (code[:1]=='\n')
: [None,-1][code[-1:]=='\n']]))
text = regex_expand_meta.sub(expand_meta, text)
text = remove_backslashes(text)
return text
def markmin2html(text,extra={},allowed={},sep='p'):
return render(text,extra,allowed,sep)
def markmin2html(text, extra={}, allowed={}, sep='p', auto=True):
return render(text, extra, allowed, sep, auto=auto)
if __name__ == '__main__':
import sys
import doctest
if sys.argv[1:2]==['-h']:
if sys.argv[1:2] == ['-h']:
print '<html><body>'+markmin2html(__doc__)+'</body></html>'
elif len(sys.argv)>1:
elif len(sys.argv) > 1:
fargv = open(sys.argv[1],'r')
try:
print '<html><body>'+markmin2html(fargv.read())+'</body></html>'