5
""" Check if it's string """
6
return isinstance(s, unicode) or isinstance(s, str)
9
def __init__(self, markdown_instance=None):
11
self.markdown = markdown_instance
13
class Treeprocessor(Processor):
15
Treeprocessors are run on the ElementTree object before serialization.
17
Each Treeprocessor implements a "run" method that takes a pointer to an
18
ElementTree, modifies it as necessary and returns an ElementTree
21
Treeprocessors must extend markdown.Treeprocessor.
26
Subclasses of Treeprocessor should implement a `run` method, which
27
takes a root ElementTree. This method can return another ElementTree
28
object, and the existing root ElementTree will be replaced, or it can
29
modify the current tree and return None.
34
class InlineProcessor(Treeprocessor):
36
A Treeprocessor that traverses a tree, applying inline patterns.
39
def __init__ (self, md):
40
self.__placeholder_prefix = markdown.INLINE_PLACEHOLDER_PREFIX
41
self.__placeholder_suffix = markdown.ETX
42
self.__placeholder_length = 4 + len(self.__placeholder_prefix) \
43
+ len(self.__placeholder_suffix)
44
self.__placeholder_re = re.compile(markdown.INLINE_PLACEHOLDER % r'([0-9]{4})')
47
def __makePlaceholder(self, type):
48
""" Generate a placeholder """
49
id = "%04d" % len(self.stashed_nodes)
50
hash = markdown.INLINE_PLACEHOLDER % id
53
def __findPlaceholder(self, data, index):
55
Extract id from data string, start from index
60
* index: index, from which we start search
62
Returns: placeholder id and string index, after the found placeholder.
65
m = self.__placeholder_re.search(data, index)
67
return m.group(1), m.end()
69
return None, index + 1
71
def __stashNode(self, node, type):
72
""" Add node to stash """
73
placeholder, id = self.__makePlaceholder(type)
74
self.stashed_nodes[id] = node
77
def __handleInline(self, data, patternIndex=0):
79
Process string with inline patterns and replace it
84
* data: A line of Markdown text
85
* patternIndex: The index of the inlinePattern to start with
87
Returns: String with placeholders.
90
if not isinstance(data, markdown.AtomicString):
92
while patternIndex < len(self.markdown.inlinePatterns):
93
data, matched, startIndex = self.__applyPattern(
94
self.markdown.inlinePatterns.value_for_index(patternIndex),
95
data, patternIndex, startIndex)
100
def __processElementText(self, node, subnode, isText=True):
102
Process placeholders in Element.text or Element.tail
103
of Elements popped from self.stashed_nodes.
108
* subnode: processing node
109
* isText: bool variable, True - it's text, False - it's tail
121
childResult = self.__processPlaceholders(text, subnode)
123
if not isText and node is not subnode:
124
pos = node.getchildren().index(subnode)
129
childResult.reverse()
130
for newChild in childResult:
131
node.insert(pos, newChild)
133
def __processPlaceholders(self, data, parent):
135
Process string with placeholders and generate ElementTree tree.
139
* data: string with placeholders instead of ElementTree elements.
140
* parent: Element, which contains processing inline data
142
Returns: list with ElementTree elements with applied inline patterns.
148
result[-1].tail += text
150
result[-1].tail = text
160
index = data.find(self.__placeholder_prefix, strartIndex)
162
id, phEndIndex = self.__findPlaceholder(data, index)
164
if id in self.stashed_nodes:
165
node = self.stashed_nodes.get(id)
168
text = data[strartIndex:index]
171
if not isString(node): # it's Element
172
for child in [node] + node.getchildren():
174
if child.tail.strip():
175
self.__processElementText(node, child, False)
177
if child.text.strip():
178
self.__processElementText(child, child)
179
else: # it's just a string
181
strartIndex = phEndIndex
184
strartIndex = phEndIndex
187
else: # wrong placeholder
188
end = index + len(prefix)
189
linkText(data[strartIndex:end])
192
text = data[strartIndex:]
198
def __applyPattern(self, pattern, data, patternIndex, startIndex=0):
200
Check if the line fits the pattern, create the necessary
201
elements, add it to stashed_nodes.
205
* data: the text to be processed
206
* pattern: the pattern to be checked
207
* patternIndex: index of current pattern
208
* startIndex: string index, from which we starting search
210
Returns: String with placeholders instead of ElementTree elements.
213
match = pattern.getCompiledRegExp().match(data[startIndex:])
214
leftData = data[:startIndex]
217
return data, False, 0
219
node = pattern.handleMatch(match)
222
return data, True, len(leftData) + match.span(len(match.groups()))[0]
224
if not isString(node):
225
if not isinstance(node.text, markdown.AtomicString):
226
# We need to process current node too
227
for child in [node] + node.getchildren():
228
if not isString(node):
230
child.text = self.__handleInline(child.text,
233
child.tail = self.__handleInline(child.tail,
236
placeholder = self.__stashNode(node, pattern.type())
238
return "%s%s%s%s" % (leftData,
240
placeholder, match.groups()[-1]), True, 0
243
"""Apply inline patterns to a parsed Markdown tree.
245
Iterate over ElementTree, find elements with inline tag, apply inline
246
patterns and append newly created Elements to tree. If you don't
247
want process your data with inline paterns, instead of normal string,
248
use subclass AtomicString:
250
node.text = markdown.AtomicString("data won't be processed with inline patterns")
254
* markdownTree: ElementTree object, representing Markdown tree.
256
Returns: ElementTree object with applied inline patterns.
259
self.stashed_nodes = {}
264
currElement = stack.pop()
266
for child in currElement.getchildren():
267
if child.text and not isinstance(child.text, markdown.AtomicString):
270
lst = self.__processPlaceholders(self.__handleInline(
273
insertQueue.append((child, lst))
275
if child.getchildren():
278
for element, lst in insertQueue:
281
markdown.inlinepatterns.handleAttributes(element.text,
285
# Processing attributes
288
markdown.inlinepatterns.handleAttributes(newChild.tail,
292
markdown.inlinepatterns.handleAttributes(newChild.text,
294
element.insert(i, newChild)
299
class PrettifyTreeprocessor(Treeprocessor):
300
""" Add linebreaks to the html document. """
302
def _prettifyETree(self, elem):
303
""" Recursively add linebreaks to ElementTree children. """
306
if markdown.isBlockLevel(elem.tag) and elem.tag not in ['code', 'pre']:
307
if (not elem.text or not elem.text.strip()) \
308
and len(elem) and markdown.isBlockLevel(elem[0].tag):
311
if markdown.isBlockLevel(e.tag):
312
self._prettifyETree(e)
313
if not elem.tail or not elem.tail.strip():
315
if not elem.tail or not elem.tail.strip():
319
""" Add linebreaks to ElementTree root object. """
321
self._prettifyETree(root)
322
# Do <br />'s seperately as they are often in the middle of
323
# inline content and missed by _prettifyETree.
324
brs = root.getiterator('br')
326
if not br.tail or not br.tail.strip():
329
br.tail = '\n%s' % br.tail