1 # markdown is released under the BSD license
2 # Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
3 # Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
4 # Copyright 2004 Manfred Stienstra (the original version)
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
16 # * Neither the name of the <organization> nor the
17 # names of its contributors may be used to endorse or promote products
18 # derived from this software without specific prior written permission.
20 # THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY
21 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 # DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT
24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
34 Admonition extension for Python-Markdown
35 ========================================
37 Adds rST-style admonitions. Inspired by [rST][] feature with the same name.
39 The syntax is (followed by an indented block with the contents):
40 !!! [type] [optional explicit title]
42 Where `type` is used as a CSS class name of the div. If not present, `title`
43 defaults to the capitalized `type`, so "note" -> "Note".
45 rST suggests the following `types`, but you're free to use whatever you want:
46 attention, caution, danger, error, hint, important, note, tip, warning
51 This is the first line inside the box.
54 <div class="admonition note">
55 <p class="admonition-title">Note</p>
56 <p>This is the first line inside the box</p>
59 You can also specify the title and CSS class of the admonition:
60 !!! custom "Did you know?"
64 <div class="admonition custom">
65 <p class="admonition-title">Did you know?</p>
66 <p>Another line here.</p>
69 [rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
71 By [Tiago Serafim](http://www.tiagoserafim.com/).
75 from __future__
import absolute_import
76 from __future__
import unicode_literals
77 from . import Extension
78 from ..blockprocessors
import BlockProcessor
79 from ..util
import etree
83 class AdmonitionExtension(Extension
):
84 """ Admonition extension for Python-Markdown. """
86 def extendMarkdown(self
, md
, md_globals
):
87 """ Add Admonition to Markdown instance. """
88 md
.registerExtension(self
)
90 md
.parser
.blockprocessors
.add('admonition',
91 AdmonitionProcessor(md
.parser
),
95 class AdmonitionProcessor(BlockProcessor
):
97 CLASSNAME
= 'admonition'
98 CLASSNAME_TITLE
= 'admonition-title'
99 RE
= re
.compile(r
'(?:^|\n)!!!\ ?([\w\-]+)(?:\ "(.*?)")?')
101 def test(self
, parent
, block
):
102 sibling
= self
.lastChild(parent
)
103 return self
.RE
.search(block
) or \
104 (block
.startswith(' ' * self
.tab_length
) and sibling
and \
105 sibling
.get('class', '').find(self
.CLASSNAME
) != -1)
107 def run(self
, parent
, blocks
):
108 sibling
= self
.lastChild(parent
)
109 block
= blocks
.pop(0)
110 m
= self
.RE
.search(block
)
113 block
= block
[m
.end() + 1:] # removes the first line
115 block
, theRest
= self
.detab(block
)
118 klass
, title
= self
.get_class_and_title(m
)
119 div
= etree
.SubElement(parent
, 'div')
120 div
.set('class', '%s %s' % (self
.CLASSNAME
, klass
))
122 p
= etree
.SubElement(div
, 'p')
124 p
.set('class', self
.CLASSNAME_TITLE
)
128 self
.parser
.parseChunk(div
, block
)
131 # This block contained unindented line(s) after the first indented
132 # line. Insert these lines as the first block of the master blocks
133 # list for future processing.
134 blocks
.insert(0, theRest
)
136 def get_class_and_title(self
, match
):
137 klass
, title
= match
.group(1).lower(), match
.group(2)
139 # no title was provided, use the capitalized classname as title
140 # e.g.: `!!! note` will render `<p class="admonition-title">Note</p>`
141 title
= klass
.capitalize()
143 # an explicit blank title should not be rendered
144 # e.g.: `!!! warning ""` will *not* render `p` with a title
149 def makeExtension(configs
={}):
150 return AdmonitionExtension(configs
=configs
)