16516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
26516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruINLINE PATTERNS
36516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru=============================================================================
46516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
56516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruInline patterns such as *emphasis* are handled by means of auxiliary
66516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruobjects, one per pattern.  Pattern objects must be instances of classes
76516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruthat extend markdown.Pattern.  Each pattern object uses a single regular
86516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruexpression and needs support the following methods:
96516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    pattern.getCompiledRegExp() # returns a regular expression
116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    pattern.handleMatch(m) # takes a match object and returns
136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                           # an ElementTree element or just plain text
146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruAll of python markdown's built-in patterns subclass from Pattern,
166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querubut you can add additional patterns that don't.
176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruAlso note that all the regular expressions used by inline must
196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querucapture the whole block.  For this reason, they all start with
206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru'^(.*)' and end with '(.*)!'.  In case with built-in expression
216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruPattern takes care of adding the "^(.*)" and "(.*)!".
226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruFinally, the order in which regular expressions are applied is very
246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimportant - e.g. if we first replace http://.../ links with <a> tags
256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruand _then_ try to replace inline html, we would end up with a mess.
266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruSo, we apply the expressions in the following order:
276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru* escape and backticks have to go before everything else, so
296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru  that we can preempt any markdown patterns by escaping them.
306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru* then we handle auto-links (must be done before inline html)
326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru* then we handle inline HTML.  At this point we will simply
346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru  replace all inline HTML strings with a placeholder and add
356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru  the actual HTML to a hash.
366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru* then inline images (must be done before links)
386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru* then bracketed links, first regular then reference-style
406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru* finally we apply strong and emphasis
426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimport markdown
456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimport re
466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querufrom urlparse import urlparse, urlunparse
476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimport sys
486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruif sys.version >= "3.0":
496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    from html import entities as htmlentitydefs
506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruelse:
516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    import htmlentitydefs
526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruThe actual regular expressions for patterns
556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru-----------------------------------------------------------------------------
566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruNOBRACKET = r'[^\]\[]*'
596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruBRK = ( r'\[('
606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        + (NOBRACKET + r'(\[')*6
616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        + (NOBRACKET+ r'\])*')*6
626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        + NOBRACKET + r')\]' )
636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruNOIMG = r'(?<!\!)'
646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruBACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)' # `e=f()` or ``e=f("`")``
666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruESCAPE_RE = r'\\(.)'                             # \<
676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruEMPHASIS_RE = r'(\*)([^\*]+)\2'                    # *emphasis*
686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruSTRONG_RE = r'(\*{2}|_{2})(.+?)\2'                      # **strong**
696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruSTRONG_EM_RE = r'(\*{3}|_{3})(.+?)\2'            # ***strong***
706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruif markdown.SMART_EMPHASIS:
726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    EMPHASIS_2_RE = r'(?<!\w)(_)(\S.+?)\2(?!\w)'        # _emphasis_
736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruelse:
746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    EMPHASIS_2_RE = r'(_)(.+?)\2'                 # _emphasis_
756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruLINK_RE = NOIMG + BRK + \
776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querur'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12)?\)'''
786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru# [text](url) or [text](<url>)
796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruIMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^\)]*))\)'
816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru# ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>)
826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruREFERENCE_RE = NOIMG + BRK+ r'\s*\[([^\]]*)\]'           # [Google][3]
836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruIMAGE_REFERENCE_RE = r'\!' + BRK + '\s*\[([^\]]*)\]' # ![alt text][2]
846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruNOT_STRONG_RE = r'((^| )(\*|_)( |$))'                        # stand-alone * or _
856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruAUTOLINK_RE = r'<((?:f|ht)tps?://[^>]*)>'        # <http://www.123.com>
866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruAUTOMAIL_RE = r'<([^> \!]*@[^> ]*)>'               # <me@example.com>
876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruHTML_RE = r'(\<([a-zA-Z/][^\>]*?|\!--.*?--)\>)'               # <...>
896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruENTITY_RE = r'(&[\#a-zA-Z0-9]*;)'               # &amp;
906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruLINE_BREAK_RE = r'  \n'                     # two spaces at end of line
916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruLINE_BREAK_2_RE = r'  $'                    # two spaces at end of text
926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querudef dequote(string):
956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """Remove quotes from around a string."""
966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    if ( ( string.startswith('"') and string.endswith('"'))
976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru         or (string.startswith("'") and string.endswith("'")) ):
986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return string[1:-1]
996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    else:
1006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return string
1016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruATTR_RE = re.compile("\{@([^\}]*)=([^\}]*)}") # {@id=123}
1036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querudef handleAttributes(text, parent):
1056516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """Set values of an element based on attribute definitions ({@id=123})."""
1066516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def attributeCallback(match):
1076516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        parent.set(match.group(1), match.group(2).replace('\n', ' '))
1086516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    return ATTR_RE.sub(attributeCallback, text)
1096516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
1126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruThe pattern classes
1136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru-----------------------------------------------------------------------------
1146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
1156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass Pattern:
1176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """Base class that inline patterns subclass. """
1186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __init__ (self, pattern, markdown_instance=None):
1206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Create an instant of an inline pattern.
1226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keyword arguments:
1246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * pattern: A regular expression that matches a pattern
1266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.pattern = pattern
1296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, re.DOTALL)
1306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        # Api for Markdown to pass safe_mode into instance
1326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.safe_mode = False
1336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if markdown_instance:
1346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            self.markdown = markdown_instance
1356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def getCompiledRegExp (self):
1376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Return a compiled regular expression. """
1386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return self.compiled_re
1396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
1416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """Return a ElementTree element from the given match.
1426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Subclasses should override this method.
1446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keyword arguments:
1466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * m: A re match object containing a match of the pattern.
1486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        pass
1516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def type(self):
1536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Return class name, to define pattern type """
1546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return self.__class__.__name__
1556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruBasePattern = Pattern # for backward compatibility
1576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass SimpleTextPattern (Pattern):
1596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Return a simple text of group(2) of a Pattern. """
1606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
1616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        text = m.group(2)
1626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if text == markdown.INLINE_PLACEHOLDER_PREFIX:
1636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return None
1646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return text
1656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass SimpleTagPattern (Pattern):
1676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
1686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Return element of type `tag` with a text attribute of group(3)
1696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    of a Pattern.
1706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
1726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __init__ (self, pattern, tag):
1736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Pattern.__init__(self, pattern)
1746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.tag = tag
1756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
1776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element(self.tag)
1786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.text = m.group(3)
1796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
1806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass SubstituteTagPattern (SimpleTagPattern):
1836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Return a eLement of type `tag` with no children. """
1846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch (self, m):
1856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return markdown.etree.Element(self.tag)
1866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass BacktickPattern (Pattern):
1896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Return a `<code>` element containing the matching text. """
1906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __init__ (self, pattern):
1916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Pattern.__init__(self, pattern)
1926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.tag = "code"
1936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
1956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element(self.tag)
1966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.text = markdown.AtomicString(m.group(3).strip())
1976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
1986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass DoubleTagPattern (SimpleTagPattern):
2016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """Return a ElementTree element nested in tag2 nested in tag1.
2026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Useful for strong emphasis etc.
2046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2056516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
2066516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
2076516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        tag1, tag2 = self.tag.split(",")
2086516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el1 = markdown.etree.Element(tag1)
2096516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el2 = markdown.etree.SubElement(el1, tag2)
2106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el2.text = m.group(3)
2116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el1
2126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass HtmlPattern (Pattern):
2156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Store raw inline html and return a placeholder. """
2166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch (self, m):
2176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        rawhtml = m.group(2)
2186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        inline = True
2196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        place_holder = self.markdown.htmlStash.store(rawhtml)
2206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return place_holder
2216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass LinkPattern (Pattern):
2246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Return a link element from the given match. """
2256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
2266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element("a")
2276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.text = m.group(2)
2286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        title = m.group(11)
2296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        href = m.group(9)
2306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if href:
2326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if href[0] == "<":
2336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                href = href[1:-1]
2346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set("href", self.sanitize_url(href.strip()))
2356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
2366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set("href", "")
2376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if title:
2396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            title = dequote(title) #.replace('"', "&quot;")
2406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set("title", title)
2416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
2426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def sanitize_url(self, url):
2446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
2456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Sanitize a url against xss attacks in "safe_mode".
2466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Rather than specifically blacklisting `javascript:alert("XSS")` and all
2486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        its aliases (see <http://ha.ckers.org/xss.html>), we whitelist known
2496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        safe url formats. Most urls contain a network location, however some
2506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        are known not to (i.e.: mailto links). Script urls do not contain a
2516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        location. Additionally, for `javascript:...`, the scheme would be
2526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        "javascript" but some aliases will appear to `urlparse()` to have no
2536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        scheme. On top of that relative links (i.e.: "foo/bar.html") have no
2546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        scheme. Therefore we must check "path", "parameters", "query" and
2556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        "fragment" for any literal colons. We don't check "scheme" for colons
2566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        because it *should* never have any and "netloc" must allow the form:
2576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        `username:password@host:port`.
2586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
2606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        locless_schemes = ['', 'mailto', 'news']
2616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        scheme, netloc, path, params, query, fragment = url = urlparse(url)
2626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        safe_url = False
2636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if netloc != '' or scheme in locless_schemes:
2646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            safe_url = True
2656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        for part in url[2:]:
2676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if ":" in part:
2686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                safe_url = False
2696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if self.markdown.safeMode and not safe_url:
2716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return ''
2726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
2736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return urlunparse(url)
2746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass ImagePattern(LinkPattern):
2766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Return a img element from the given match. """
2776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
2786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element("img")
2796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        src_parts = m.group(9).split()
2806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if src_parts:
2816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            src = src_parts[0]
2826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if src[0] == "<" and src[-1] == ">":
2836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                src = src[1:-1]
2846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set('src', self.sanitize_url(src))
2856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
2866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set('src', "")
2876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if len(src_parts) > 1:
2886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set('title', dequote(" ".join(src_parts[1:])))
2896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if markdown.ENABLE_ATTRIBUTES:
2916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            truealt = handleAttributes(m.group(2), el)
2926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
2936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            truealt = m.group(2)
2946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.set('alt', truealt)
2966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
2976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass ReferencePattern(LinkPattern):
2996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Match to a stored reference and return link element. """
3006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
3016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if m.group(9):
3026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            id = m.group(9).lower()
3036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
3046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            # if we got something like "[Google][]"
3056516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            # we'll use "google" as the id
3066516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            id = m.group(2).lower()
3076516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3086516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not id in self.markdown.references: # ignore undefined refs
3096516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return None
3106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        href, title = self.markdown.references[id]
3116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        text = m.group(2)
3136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return self.makeTag(href, title, text)
3146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def makeTag(self, href, title, text):
3166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element('a')
3176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.set('href', self.sanitize_url(href))
3196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if title:
3206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set('title', title)
3216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.text = text
3236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
3246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass ImageReferencePattern (ReferencePattern):
3276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Match to a stored reference and return img element. """
3286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def makeTag(self, href, title, text):
3296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element("img")
3306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.set("src", self.sanitize_url(href))
3316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if title:
3326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            el.set("title", title)
3336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.set("alt", text)
3346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
3356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass AutolinkPattern (Pattern):
3386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Return a link Element given an autolink (`<http://example/com>`). """
3396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
3406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element("a")
3416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.set('href', m.group(2))
3426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.text = markdown.AtomicString(m.group(2))
3436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
3446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass AutomailPattern (Pattern):
3466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
3476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Return a mailto link Element given an automail link (`<foo@example.com>`).
3486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
3496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def handleMatch(self, m):
3506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el = markdown.etree.Element('a')
3516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        email = m.group(2)
3526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if email.startswith("mailto:"):
3536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            email = email[len("mailto:"):]
3546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        def codepoint2name(code):
3566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            """Return entity definition by code, or the code if not defined."""
3576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            entity = htmlentitydefs.codepoint2name.get(code)
3586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if entity:
3596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                return "%s%s;" % (markdown.AMP_SUBSTITUTE, entity)
3606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            else:
3616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                return "%s#%d;" % (markdown.AMP_SUBSTITUTE, code)
3626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        letters = [codepoint2name(ord(letter)) for letter in email]
3646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.text = markdown.AtomicString(''.join(letters))
3656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        mailto = "mailto:" + email
3676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        mailto = "".join([markdown.AMP_SUBSTITUTE + '#%d;' %
3686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                          ord(letter) for letter in mailto])
3696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        el.set('href', mailto)
3706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return el
3716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
372