15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# markdown is released under the BSD license
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2004 Manfred Stienstra (the original version)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# All rights reserved.
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Redistribution and use in source and binary forms, with or without
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# modification, are permitted provided that the following conditions are met:
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# *   Redistributions of source code must retain the above copyright
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     notice, this list of conditions and the following disclaimer.
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# *   Redistributions in binary form must reproduce the above copyright
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     notice, this list of conditions and the following disclaimer in the
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     documentation and/or other materials provided with the distribution.
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# *   Neither the name of the <organization> nor the
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     names of its contributors may be used to endorse or promote products
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#     derived from this software without specific prior written permission.
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# POSSIBILITY OF SUCH DAMAGE.
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)"""
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Tables Extension for Python-Markdown
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)====================================
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Added parsing of tables to Python-Markdown.
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)A simple example:
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    First Header  | Second Header
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ------------- | -------------
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Content Cell  | Content Cell
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Content Cell  | Content Cell
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Copyright 2009 - [Waylan Limberg](http://achinghead.com)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)"""
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from __future__ import absolute_import
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from __future__ import unicode_literals
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from . import Extension
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ..blockprocessors import BlockProcessor
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ..util import etree
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class TableProcessor(BlockProcessor):
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """ Process Tables. """
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def test(self, parent, block):
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        rows = block.split('\n')
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return (len(rows) > 2 and '|' in rows[0] and
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                '|' in rows[1] and '-' in rows[1] and
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                rows[1].strip()[0] in ['|', ':', '-'])
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def run(self, parent, blocks):
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """ Parse a table block and build table. """
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        block = blocks.pop(0).split('\n')
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        header = block[0].strip()
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        seperator = block[1].strip()
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        rows = block[2:]
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # Get format type (bordered by pipes or not)
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        border = False
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if header.startswith('|'):
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            border = True
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # Get alignment of columns
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        align = []
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for c in self._split_row(seperator, border):
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if c.startswith(':') and c.endswith(':'):
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                align.append('center')
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            elif c.startswith(':'):
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                align.append('left')
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            elif c.endswith(':'):
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                align.append('right')
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            else:
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                align.append(None)
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # Build table
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        table = etree.SubElement(parent, 'table')
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        thead = etree.SubElement(table, 'thead')
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        self._build_row(header, thead, align, border)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        tbody = etree.SubElement(table, 'tbody')
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for row in rows:
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            self._build_row(row.strip(), tbody, align, border)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def _build_row(self, row, parent, align, border):
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """ Given a row of text, build table cells. """
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        tr = etree.SubElement(parent, 'tr')
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        tag = 'td'
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if parent.tag == 'thead':
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            tag = 'th'
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        cells = self._split_row(row, border)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # We use align here rather than cells to ensure every row
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        # contains the same number of columns.
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for i, a in enumerate(align):
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            c = etree.SubElement(tr, tag)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            try:
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                c.text = cells[i].strip()
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            except IndexError:
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                c.text = ""
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if a:
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                c.set('align', a)
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def _split_row(self, row, border):
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """ split a row of text into list of cells. """
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if border:
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if row.startswith('|'):
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                row = row[1:]
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if row.endswith('|'):
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                row = row[:-1]
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return row.split('|')
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class TableExtension(Extension):
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    """ Add tables to Markdown. """
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def extendMarkdown(self, md, md_globals):
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        """ Add an instance of TableProcessor to BlockParser. """
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        md.parser.blockprocessors.add('table',
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                      TableProcessor(md.parser),
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                      '<hashheader')
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def makeExtension(configs={}):
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return TableExtension(configs=configs)
133