1#!/usr/bin/env Python
2"""
3Tables Extension for Python-Markdown
4====================================
5
6Added parsing of tables to Python-Markdown.
7
8A simple example:
9
10    First Header  | Second Header
11    ------------- | -------------
12    Content Cell  | Content Cell
13    Content Cell  | Content Cell
14
15Copyright 2009 - [Waylan Limberg](http://achinghead.com)
16"""
17import markdown
18from markdown import etree
19
20
21class TableProcessor(markdown.blockprocessors.BlockProcessor):
22    """ Process Tables. """
23
24    def test(self, parent, block):
25        rows = block.split('\n')
26        return (len(rows) > 2 and '|' in rows[0] and
27                '|' in rows[1] and '-' in rows[1] and
28                rows[1][0] in ['|', ':', '-'])
29
30    def run(self, parent, blocks):
31        """ Parse a table block and build table. """
32        block = blocks.pop(0).split('\n')
33        header = block[:2]
34        rows = block[2:]
35        # Get format type (bordered by pipes or not)
36        border = False
37        if header[0].startswith('|'):
38            border = True
39        # Get alignment of columns
40        align = []
41        for c in self._split_row(header[1], border):
42            if c.startswith(':') and c.endswith(':'):
43                align.append('center')
44            elif c.startswith(':'):
45                align.append('left')
46            elif c.endswith(':'):
47                align.append('right')
48            else:
49                align.append(None)
50        # Build table
51        table = etree.SubElement(parent, 'table')
52        thead = etree.SubElement(table, 'thead')
53        self._build_row(header[0], thead, align, border)
54        tbody = etree.SubElement(table, 'tbody')
55        for row in rows:
56            self._build_row(row, tbody, align, border)
57
58    def _build_row(self, row, parent, align, border):
59        """ Given a row of text, build table cells. """
60        tr = etree.SubElement(parent, 'tr')
61        tag = 'td'
62        if parent.tag == 'thead':
63            tag = 'th'
64        cells = self._split_row(row, border)
65        # We use align here rather than cells to ensure every row
66        # contains the same number of columns.
67        for i, a in enumerate(align):
68            c = etree.SubElement(tr, tag)
69            try:
70                c.text = cells[i].strip()
71            except IndexError:
72                c.text = ""
73            if a:
74                c.set('align', a)
75
76    def _split_row(self, row, border):
77        """ split a row of text into list of cells. """
78        if border:
79            if row.startswith('|'):
80                row = row[1:]
81            if row.endswith('|'):
82                row = row[:-1]
83        return row.split('|')
84
85
86class TableExtension(markdown.Extension):
87    """ Add tables to Markdown. """
88
89    def extendMarkdown(self, md, md_globals):
90        """ Add an instance of TableProcessor to BlockParser. """
91        md.parser.blockprocessors.add('table',
92                                      TableProcessor(md.parser),
93                                      '<hashheader')
94
95
96def makeExtension(configs={}):
97    return TableExtension(configs=configs)
98