15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# markdown is released under the BSD license
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2004 Manfred Stienstra (the original version)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# All rights reserved.
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Redistribution and use in source and binary forms, with or without
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# modification, are permitted provided that the following conditions are met:
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# *   Redistributions of source code must retain the above copyright
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     notice, this list of conditions and the following disclaimer.
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# *   Redistributions in binary form must reproduce the above copyright
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     notice, this list of conditions and the following disclaimer in the
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     documentation and/or other materials provided with the distribution.
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# *   Neither the name of the <organization> nor the
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     names of its contributors may be used to endorse or promote products
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     derived from this software without specific prior written permission.
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# POSSIBILITY OF SUCH DAMAGE.
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)"""
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)POST-PROCESSORS
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)=============================================================================
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Markdown also allows post-processors, which are similar to preprocessors in
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)that they need to implement a "run" method. However, they are run after core
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)processing.
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)"""
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from __future__ import absolute_import
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from __future__ import unicode_literals
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from . import util
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from . import odict
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import re
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def build_postprocessors(md_instance, **kwargs):
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """ Build the default postprocessors for Markdown. """
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    postprocessors = odict.OrderedDict()
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    postprocessors["raw_html"] = RawHtmlPostprocessor(md_instance)
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    postprocessors["amp_substitute"] = AndSubstitutePostprocessor()
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    postprocessors["unescape"] = UnescapePostprocessor()
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return postprocessors
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class Postprocessor(util.Processor):
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Postprocessors are run after the ElementTree it converted back into text.
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Each Postprocessor implements a "run" method that takes a pointer to a
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    text string, modifies it as necessary and returns a text string.
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Postprocessors must extend markdown.Postprocessor.
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def run(self, text):
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Subclasses of Postprocessor should implement a `run` method, which
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        takes the html document as a single text string and returns a
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        (possibly modified) string.
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        pass
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class RawHtmlPostprocessor(Postprocessor):
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """ Restore raw html to the document. """
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def run(self, text):
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """ Iterate over html stash and restore "safe" html. """
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for i in range(self.markdown.htmlStash.html_counter):
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            html, safe  = self.markdown.htmlStash.rawHtmlBlocks[i]
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if self.markdown.safeMode and not safe:
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if str(self.markdown.safeMode).lower() == 'escape':
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    html = self.escape(html)
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                elif str(self.markdown.safeMode).lower() == 'remove':
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    html = ''
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                else:
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    html = self.markdown.html_replacement_text
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if self.isblocklevel(html) and (safe or not self.markdown.safeMode):
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                text = text.replace("<p>%s</p>" %
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            (self.markdown.htmlStash.get_placeholder(i)),
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            html + "\n")
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            text =  text.replace(self.markdown.htmlStash.get_placeholder(i),
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 html)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return text
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def escape(self, html):
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """ Basic html escaping """
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        html = html.replace('&', '&amp;')
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        html = html.replace('<', '&lt;')
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        html = html.replace('>', '&gt;')
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return html.replace('"', '&quot;')
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def isblocklevel(self, html):
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        m = re.match(r'^\<\/?([^ >]+)', html)
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if m:
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if m.group(1)[0] in ('!', '?', '@', '%'):
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                # Comment, php etc...
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                return True
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return util.isBlockLevel(m.group(1))
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return False
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class AndSubstitutePostprocessor(Postprocessor):
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """ Restore valid entities """
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def run(self, text):
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        text =  text.replace(util.AMP_SUBSTITUTE, "&")
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return text
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class UnescapePostprocessor(Postprocessor):
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """ Restore escaped chars """
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RE = re.compile('%s(\d+)%s' % (util.STX, util.ETX))
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def unescape(self, m):
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return util.int2str(int(m.group(1)))
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def run(self, text):
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return self.RE.sub(self.unescape, text)
137