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)Attribute List Extension for Python-Markdown 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)============================================ 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Adds attribute list syntax. Inspired by 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)[maruku](http://maruku.rubyforge.org/proposal.html#attribute_lists)'s 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)feature of the same name. 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Copyright 2011 [Waylan Limberg](http://achinghead.com/). 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Contact: markdown@freewisdom.org 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)License: BSD (see ../LICENSE.md for details) 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)Dependencies: 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)* [Python 2.4+](http://python.org) 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)* [Markdown 2.1+](http://packages.python.org/Markdown/) 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)""" 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from __future__ import absolute_import 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from __future__ import unicode_literals 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from . import Extension 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ..treeprocessors import Treeprocessor 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from ..util import isBlockLevel 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import re 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)try: 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Scanner = re.Scanner 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)except AttributeError: 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # must be on Python 2.4 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) from sre import Scanner 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def _handle_double_quote(s, t): 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) k, v = t.split('=') 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return k, v.strip('"') 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def _handle_single_quote(s, t): 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) k, v = t.split('=') 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return k, v.strip("'") 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def _handle_key_value(s, t): 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return t.split('=') 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def _handle_word(s, t): 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if t.startswith('.'): 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return '.', t[1:] 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if t.startswith('#'): 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return 'id', t[1:] 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return t, t 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)_scanner = Scanner([ 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (r'[^ ]+=".*?"', _handle_double_quote), 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (r"[^ ]+='.*?'", _handle_single_quote), 875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (r'[^ ]+=[^ ]*', _handle_key_value), 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (r'[^ ]+', _handle_word), 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (r' ', None) 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)]) 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def get_attrs(str): 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """ Parse attribute list and return a list of attribute tuples. """ 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return _scanner.scan(str)[0] 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def isheader(elem): 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return elem.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class AttrListTreeprocessor(Treeprocessor): 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) BASE_RE = r'\{\:?([^\}]*)\}' 1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) HEADER_RE = re.compile(r'[ ]*%s[ ]*$' % BASE_RE) 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) BLOCK_RE = re.compile(r'\n[ ]*%s[ ]*$' % BASE_RE) 1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) INLINE_RE = re.compile(r'^%s' % BASE_RE) 1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NAME_RE = re.compile(r'[^A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff\u0370-\u037d' 1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) r'\u037f-\u1fff\u200c-\u200d\u2070-\u218f\u2c00-\u2fef' 1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) r'\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd' 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+') 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def run(self, doc): 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for elem in doc.getiterator(): 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if isBlockLevel(elem.tag): 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # Block level: check for attrs on last line of text 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RE = self.BLOCK_RE 1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if isheader(elem): 1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # header: check for attrs at end of line 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RE = self.HEADER_RE 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if len(elem) and elem[-1].tail: 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # has children. Get from tail of last child 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) m = RE.search(elem[-1].tail) 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if m: 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self.assign_attrs(elem, m.group(1)) 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem[-1].tail = elem[-1].tail[:m.start()] 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if isheader(elem): 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # clean up trailing #s 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem[-1].tail = elem[-1].tail.rstrip('#').rstrip() 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elif elem.text: 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # no children. Get from text. 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) m = RE.search(elem.text) 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if m: 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self.assign_attrs(elem, m.group(1)) 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem.text = elem.text[:m.start()] 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if isheader(elem): 1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # clean up trailing #s 1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem.text = elem.text.rstrip('#').rstrip() 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else: 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # inline: check for attrs at start of tail 1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if elem.tail: 1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) m = self.INLINE_RE.match(elem.tail) 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if m: 1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) self.assign_attrs(elem, m.group(1)) 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem.tail = elem.tail[m.end():] 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def assign_attrs(self, elem, attrs): 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """ Assign attrs to element. """ 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for k, v in get_attrs(attrs): 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if k == '.': 1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # add to class 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) cls = elem.get('class') 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if cls: 1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem.set('class', '%s %s' % (cls, v)) 1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else: 1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem.set('class', v) 1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else: 1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) # assign attr k with v 1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) elem.set(self.sanitize_name(k), v) 1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def sanitize_name(self, name): 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """ 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) Sanitize name as 'an XML Name, minus the ":"'. 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) See http://www.w3.org/TR/REC-xml-names/#NT-NCName 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) """ 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return self.NAME_RE.sub('_', name) 1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class AttrListExtension(Extension): 1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) def extendMarkdown(self, md, md_globals): 1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) md.treeprocessors.add('attr_list', AttrListTreeprocessor(md), '>prettify') 1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def makeExtension(configs={}): 1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return AttrListExtension(configs=configs) 173