inlinepatterns.py revision 6516b99bb74dfb7187a08f7090bf7ca22a006f15
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]*;)' # & 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('"', """) 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