16516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimport markdown
26516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimport re
36516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
46516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querudef isString(s):
56516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Check if it's string """
66516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    return isinstance(s, unicode) or isinstance(s, str)
76516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
86516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass Processor:
96516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __init__(self, markdown_instance=None):
106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if markdown_instance:
116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            self.markdown = markdown_instance
126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass Treeprocessor(Processor):
146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Treeprocessors are run on the ElementTree object before serialization.
166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Each Treeprocessor implements a "run" method that takes a pointer to an
186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    ElementTree, modifies it as necessary and returns an ElementTree
196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    object.
206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Treeprocessors must extend markdown.Treeprocessor.
226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def run(self, root):
256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Subclasses of Treeprocessor should implement a `run` method, which
276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        takes a root ElementTree. This method can return another ElementTree
286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        object, and the existing root ElementTree will be replaced, or it can
296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        modify the current tree and return None.
306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        pass
326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass InlineProcessor(Treeprocessor):
356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    A Treeprocessor that traverses a tree, applying inline patterns.
376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """
386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __init__ (self, md):
406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.__placeholder_prefix = markdown.INLINE_PLACEHOLDER_PREFIX
416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.__placeholder_suffix = markdown.ETX
426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.__placeholder_length = 4 + len(self.__placeholder_prefix) \
436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                      + len(self.__placeholder_suffix)
446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.__placeholder_re = re.compile(markdown.INLINE_PLACEHOLDER % r'([0-9]{4})')
456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.markdown = md
466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __makePlaceholder(self, type):
486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Generate a placeholder """
496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        id = "%04d" % len(self.stashed_nodes)
506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        hash = markdown.INLINE_PLACEHOLDER % id
516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return hash, id
526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __findPlaceholder(self, data, index):
546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Extract id from data string, start from index
566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keyword arguments:
586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * data: string
606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * index: index, from which we start search
616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Returns: placeholder id and string index, after the found placeholder.
636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        m = self.__placeholder_re.search(data, index)
666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if m:
676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return m.group(1), m.end()
686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return None, index + 1
706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __stashNode(self, node, type):
726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Add node to stash """
736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        placeholder, id = self.__makePlaceholder(type)
746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.stashed_nodes[id] = node
756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return placeholder
766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __handleInline(self, data, patternIndex=0):
786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Process string with inline patterns and replace it
806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        with placeholders
816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keyword arguments:
836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * data: A line of Markdown text
856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * patternIndex: The index of the inlinePattern to start with
866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Returns: String with placeholders.
886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not isinstance(data, markdown.AtomicString):
916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            startIndex = 0
926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            while patternIndex < len(self.markdown.inlinePatterns):
936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                data, matched, startIndex = self.__applyPattern(
946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    self.markdown.inlinePatterns.value_for_index(patternIndex),
956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    data, patternIndex, startIndex)
966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if not matched:
976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    patternIndex += 1
986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return data
996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __processElementText(self, node, subnode, isText=True):
1016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Process placeholders in Element.text or Element.tail
1036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        of Elements popped from self.stashed_nodes.
1046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1056516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keywords arguments:
1066516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1076516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * node: parent node
1086516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * subnode: processing node
1096516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * isText: bool variable, True - it's text, False - it's tail
1106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Returns: None
1126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if isText:
1156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            text = subnode.text
1166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            subnode.text = None
1176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
1186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            text = subnode.tail
1196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            subnode.tail = None
1206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        childResult = self.__processPlaceholders(text, subnode)
1226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not isText and node is not subnode:
1246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            pos = node.getchildren().index(subnode)
1256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            node.remove(subnode)
1266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
1276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            pos = 0
1286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        childResult.reverse()
1306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        for newChild in childResult:
1316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            node.insert(pos, newChild)
1326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __processPlaceholders(self, data, parent):
1346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Process string with placeholders and generate ElementTree tree.
1366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keyword arguments:
1386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * data: string with placeholders instead of ElementTree elements.
1406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * parent: Element, which contains processing inline data
1416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Returns: list with ElementTree elements with applied inline patterns.
1436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
1446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        def linkText(text):
1456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if text:
1466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if result:
1476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if result[-1].tail:
1486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        result[-1].tail += text
1496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    else:
1506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        result[-1].tail = text
1516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                else:
1526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if parent.text:
1536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        parent.text += text
1546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    else:
1556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        parent.text = text
1566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        result = []
1586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        strartIndex = 0
1596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        while data:
1606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            index = data.find(self.__placeholder_prefix, strartIndex)
1616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if index != -1:
1626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                id, phEndIndex = self.__findPlaceholder(data, index)
1636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if id in self.stashed_nodes:
1656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    node = self.stashed_nodes.get(id)
1666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if index > 0:
1686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        text = data[strartIndex:index]
1696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        linkText(text)
1706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if not isString(node): # it's Element
1726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        for child in [node] + node.getchildren():
1736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                            if child.tail:
1746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                if child.tail.strip():
1756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                    self.__processElementText(node, child, False)
1766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                            if child.text:
1776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                if child.text.strip():
1786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                    self.__processElementText(child, child)
1796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    else: # it's just a string
1806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        linkText(node)
1816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        strartIndex = phEndIndex
1826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        continue
1836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    strartIndex = phEndIndex
1856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    result.append(node)
1866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                else: # wrong placeholder
1886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    end = index + len(prefix)
1896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    linkText(data[strartIndex:end])
1906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    strartIndex = end
1916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            else:
1926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                text = data[strartIndex:]
1936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                linkText(text)
1946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                data = ""
1956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return result
1976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def __applyPattern(self, pattern, data, patternIndex, startIndex=0):
1996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
2006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Check if the line fits the pattern, create the necessary
2016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        elements, add it to stashed_nodes.
2026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Keyword arguments:
2046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2056516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * data: the text to be processed
2066516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * pattern: the pattern to be checked
2076516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * patternIndex: index of current pattern
2086516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * startIndex: string index, from which we starting search
2096516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Returns: String with placeholders instead of ElementTree elements.
2116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
2136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        match = pattern.getCompiledRegExp().match(data[startIndex:])
2146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        leftData = data[:startIndex]
2156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not match:
2176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return data, False, 0
2186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        node = pattern.handleMatch(match)
2206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if node is None:
2226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            return data, True, len(leftData) + match.span(len(match.groups()))[0]
2236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not isString(node):
2256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if not isinstance(node.text, markdown.AtomicString):
2266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                # We need to process current node too
2276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                for child in [node] + node.getchildren():
2286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if not isString(node):
2296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        if child.text:
2306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                            child.text = self.__handleInline(child.text,
2316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                                            patternIndex + 1)
2326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        if child.tail:
2336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                            child.tail = self.__handleInline(child.tail,
2346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                                            patternIndex)
2356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        placeholder = self.__stashNode(node, pattern.type())
2376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return "%s%s%s%s" % (leftData,
2396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                             match.group(1),
2406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                             placeholder, match.groups()[-1]), True, 0
2416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def run(self, tree):
2436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """Apply inline patterns to a parsed Markdown tree.
2446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Iterate over ElementTree, find elements with inline tag, apply inline
2466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        patterns and append newly created Elements to tree.  If you don't
2476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        want process your data with inline paterns, instead of normal string,
2486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        use subclass AtomicString:
2496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            node.text = markdown.AtomicString("data won't be processed with inline patterns")
2516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Arguments:
2536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        * markdownTree: ElementTree object, representing Markdown tree.
2556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        Returns: ElementTree object with applied inline patterns.
2576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """
2596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.stashed_nodes = {}
2606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        stack = [tree]
2626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        while stack:
2646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            currElement = stack.pop()
2656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            insertQueue = []
2666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            for child in currElement.getchildren():
2676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if child.text and not isinstance(child.text, markdown.AtomicString):
2686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    text = child.text
2696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    child.text = None
2706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    lst = self.__processPlaceholders(self.__handleInline(
2716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                                    text), child)
2726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    stack += lst
2736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    insertQueue.append((child, lst))
2746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if child.getchildren():
2766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    stack.append(child)
2776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            for element, lst in insertQueue:
2796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if element.text:
2806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    element.text = \
2816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        markdown.inlinepatterns.handleAttributes(element.text,
2826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                                                 element)
2836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                i = 0
2846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                for newChild in lst:
2856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    # Processing attributes
2866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if newChild.tail:
2876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        newChild.tail = \
2886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                            markdown.inlinepatterns.handleAttributes(newChild.tail,
2896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                                                     element)
2906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    if newChild.text:
2916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                        newChild.text = \
2926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                            markdown.inlinepatterns.handleAttributes(newChild.text,
2936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                                                     newChild)
2946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    element.insert(i, newChild)
2956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    i += 1
2966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return tree
2976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
2996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass PrettifyTreeprocessor(Treeprocessor):
3006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Add linebreaks to the html document. """
3016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def _prettifyETree(self, elem):
3036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Recursively add linebreaks to ElementTree children. """
3046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3056516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        i = "\n"
3066516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if markdown.isBlockLevel(elem.tag) and elem.tag not in ['code', 'pre']:
3076516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if (not elem.text or not elem.text.strip()) \
3086516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    and len(elem) and markdown.isBlockLevel(elem[0].tag):
3096516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                elem.text = i
3106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            for e in elem:
3116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                if markdown.isBlockLevel(e.tag):
3126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                    self._prettifyETree(e)
3136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if not elem.tail or not elem.tail.strip():
3146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                elem.tail = i
3156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not elem.tail or not elem.tail.strip():
3166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            elem.tail = i
3176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def run(self, root):
3196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Add linebreaks to ElementTree root object. """
3206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
3216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self._prettifyETree(root)
3226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        # Do <br />'s seperately as they are often in the middle of
3236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        # inline content and missed by _prettifyETree.
3246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        brs = root.getiterator('br')
3256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        for br in brs:
3266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if not br.tail or not br.tail.strip():
3276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                br.tail = '\n'
3286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            else:
3296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                br.tail = '\n%s' % br.tail
330