textile-2.2.2/0000755000076700000240000000000012462765643013617 5ustar dennisstaff00000000000000textile-2.2.2/PKG-INFO0000644000076700000240000000115112462765643014712 0ustar dennisstaff00000000000000Metadata-Version: 1.1 Name: textile Version: 2.2.2 Summary: Textile processing for python. Home-page: http://github.com/textile/python-textile Author: UNKNOWN Author-email: UNKNOWN License: UNKNOWN Description: UNKNOWN Keywords: textile,text Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules textile-2.2.2/setup.cfg0000644000076700000240000000032012462765643015433 0ustar dennisstaff00000000000000[nosetests] detailed-errors = 1 with-coverage = 1 cover-package = textile cover-erase = 1 with-doctest = 1 with-id = 1 [bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 textile-2.2.2/setup.py0000644000076700000240000000236412462764257015336 0ustar dennisstaff00000000000000from setuptools import setup, find_packages import os import sys install_requires = [] if 'develop' in sys.argv: install_requires.extend([ 'tox', ]) def get_version(): basedir = os.path.dirname(__file__) with open(os.path.join(basedir, 'textile/version.py')) as f: variables = {} exec(f.read(), variables) return variables.get('VERSION') raise RuntimeError('No version info found.') setup( name='textile', version=get_version(), description='Textile processing for python.', url='http://github.com/textile/python-textile', packages=find_packages(), classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Software Development :: Libraries :: Python Modules', ], keywords='textile,text', install_requires=install_requires, extras_require={ ':python_version=="2.6"': ['ordereddict>=1.1'], }, test_suite='nose.collector', tests_require=['nose'], include_package_data=True, zip_safe=False, ) textile-2.2.2/textile/0000755000076700000240000000000012462765643015275 5ustar dennisstaff00000000000000textile-2.2.2/textile/__init__.py0000644000076700000240000000030112462764257017400 0ustar dennisstaff00000000000000from __future__ import unicode_literals from .core import textile, textile_restricted, Textile from .version import VERSION __all__ = ['textile', 'textile_restricted'] __version__ = VERSION textile-2.2.2/textile/core.py0000644000076700000240000016634312462764512016606 0ustar dennisstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import unicode_literals __copyright__ = """ Copyright (c) 2009, Jason Samsa, http://jsamsa.com/ Copyright (c) 2010, Kurt Raschke Copyright (c) 2004, Roberto A. F. De Almeida, http://dealmeida.net/ Copyright (c) 2003, Mark Pilgrim, http://diveintomark.org/ Original PHP Version: Copyright (c) 2003-2004, Dean Allen All rights reserved. Thanks to Carlo Zottmann for refactoring Textile's procedural code into a class framework Additions and fixes Copyright (c) 2006 Alex Shiels http://thresholdstate.com/ """ import uuid from textile.tools import sanitizer, imagesize # We're going to use the Python 2.7+ OrderedDict data type. Import it if it's # available, otherwise, use the included tool. try: from collections import OrderedDict except ImportError: from ordereddict import OrderedDict try: # Python 3 from urllib.parse import urlparse, urlsplit, urlunsplit, quote, unquote from html.parser import HTMLParser xrange = range unichr = chr unicode = str except (ImportError): # Python 2 from urllib import quote, unquote from urlparse import urlparse, urlsplit, urlunsplit from HTMLParser import HTMLParser try: # Use regex module for matching uppercase characters if installed, # otherwise fall back to finding all the uppercase chars in a loop. import regex as re upper_re_s = r'\p{Lu}' except ImportError: import re from sys import maxunicode upper_re_s = "".join([unichr(c) for c in xrange(maxunicode) if unichr(c).isupper()]) def _normalize_newlines(string): out = string.strip() out = re.sub(r'\r\n', '\n', out) out = re.sub(r'\n{3,}', '\n\n', out) out = re.sub(r'\n\s*\n', '\n\n', out) out = re.sub(r'"$', '" ', out) return out class Textile(object): halign_re_s = r'(?:\<(?!>)|(?|\<\>|\=|[()]+(?! ))' valign_re_s = r'[\-^~]' class_re_s = r'(?:\([^)\n]+\))' # Don't allow classes/ids, language_re_s = r'(?:\[[^\]\n]+\])' # languages, style_re_s = r'(?:\{[^}\n]+\})' # or styles to span across newlines colspan_re_s = r'(?:\\\d+)' rowspan_re_s = r'(?:\/\d+)' align_re_s = r'(?:%s|%s)*' % (halign_re_s, valign_re_s) table_span_re_s = r'(?:%s|%s)*' % (colspan_re_s, rowspan_re_s) # regex string to match class, style, language and horizontal alignment # attributes cslh_re_s = r'(?:%s)*' % '|'.join([class_re_s, style_re_s, language_re_s, halign_re_s]) # regex string to match class, style and language attributes csl_re_s = r'(?:%s)*' % '|'.join([class_re_s, style_re_s, language_re_s]) pnct_re_s = r'[-!"#$%&()*+,/:;<=>?@\'\[\\\]\.^_`{|}~]' urlchar_re_s = r'[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]' syms_re_s = '¤§µ¶†‡•∗∴◊♠♣♥♦' restricted_url_schemes = ('http', 'https', 'ftp', 'mailto') unrestricted_url_schemes = restricted_url_schemes + ('file', 'tel', 'callto', 'sftp') btag = ('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p', '###') btag_lite = ('bq', 'bc', 'p') iAlign = {'<': 'float: left;', '>': 'float: right;', '=': 'display: block; margin: 0 auto;'} vAlign = {'^': 'top', '-': 'middle', '~': 'bottom'} hAlign = {'<': 'left', '=': 'center', '>': 'right', '<>': 'justify'} note_index = 1 doctype_whitelist = ['xhtml', 'html5'] glyph_definitions = { 'quote_single_open': '‘', 'quote_single_close': '’', 'quote_double_open': '“', 'quote_double_close': '”', 'apostrophe': '’', 'prime': '′', 'prime_double': '″', 'ellipsis': '…', 'ampersand': '&', 'emdash': '—', 'endash': '–', 'dimension': '×', 'trademark': '™', 'registered': '®', 'copyright': '©', 'half': '½', 'quarter': '¼', 'threequarters': '¾', 'degrees': '°', 'plusminus': '±', 'fn_ref_pattern': '%(marker)s', 'fn_foot_pattern': '%(marker)s', 'nl_ref_pattern': '%(marker)s', } def __init__(self, restricted=False, lite=False, noimage=False, auto_link=False, get_sizes=False, html_type='xhtml'): """Textile properties that are common to regular textile and textile_restricted""" self.restricted = restricted self.lite = lite self.noimage = noimage self.get_sizes = get_sizes self.auto_link = auto_link self.fn = {} self.urlrefs = {} self.shelf = {} self.rel = '' self.html_type = html_type self.max_span_depth = 5 # We'll be searching for characters that need to be HTML-encoded to # produce properly valid html. These are the defaults that work in # most cases. Below, we'll copy this and modify the necessary pieces # to make it work for characters at the beginning of the string. self.glyph_search = [ # apostrophe's re.compile(r"(^|\w)'(\w)", re.U), # back in '88 re.compile(r"(\s)'(\d+\w?)\b(?!')", re.U), # single closing re.compile(r"(^|\S)'(?=\s|%s|$)" % self.pnct_re_s, re.U), # single opening re.compile(r"'", re.U), # double closing re.compile(r'(^|\S)"(?=\s|%s|$)' % self.pnct_re_s, re.U), # double opening re.compile(r'"'), # ellipsis re.compile(r'([^.]?)\.{3}', re.U), # ampersand re.compile(r'(\s)&(\s)', re.U), # em dash re.compile(r'(\s?)--(\s?)', re.U), # en dash re.compile(r'\s-(?:\s|$)', re.U), # dimension sign re.compile(r'(\d+)( ?)x( ?)(?=\d+)', re.U), # trademark re.compile(r'\b ?[([]TM[])]', re.I | re.U), # registered re.compile(r'\b ?[([]R[])]', re.I | re.U), # copyright re.compile(r'\b ?[([]C[])]', re.I | re.U), # 1/2 re.compile(r'[([]1\/2[])]', re.I | re.U), # 1/4 re.compile(r'[([]1\/4[])]', re.I | re.U), # 3/4 re.compile(r'[([]3\/4[])]', re.I | re.U), # degrees re.compile(r'[([]o[])]', re.I | re.U), # plus/minus re.compile(r'[([]\+\/-[])]', re.I | re.U), # 3+ uppercase acronym re.compile(r'\b([%s][%s0-9]{2,})\b(?:[(]([^)]*)[)])' % (upper_re_s, upper_re_s)), # 3+ uppercase re.compile(r"""(?:(?<=^)|(?<=\s)|(?<=[>\(;-]))([%s]{3,})(\w*)(?=\s|%s|$)(?=[^">]*?(<|$))""" % (upper_re_s, self.pnct_re_s)), ] # These are the changes that need to be made for characters that occur # at the beginning of the string. self.glyph_search_initial = list(self.glyph_search) # apostrophe's self.glyph_search_initial[0] = re.compile(r"(\w)'(\w)", re.U) # single closing self.glyph_search_initial[2] = re.compile(r"(\S)'(?=\s|%s|$)" % self.pnct_re_s, re.U) # double closing self.glyph_search_initial[4] = re.compile(r'(\S)"(?=\s|%s|$)' % self.pnct_re_s, re.U) self.glyph_replace = [x % self.glyph_definitions for x in ( r'\1%(apostrophe)s\2', # apostrophe's r'\1%(apostrophe)s\2', # back in '88 r'\1%(quote_single_close)s', # single closing r'%(quote_single_open)s', # single opening r'\1%(quote_double_close)s', # double closing r'%(quote_double_open)s', # double opening r'\1%(ellipsis)s', # ellipsis r'\1%(ampersand)s\2', # ampersand r'\1%(emdash)s\2', # em dash r' %(endash)s ', # en dash r'\1\2%(dimension)s\3', # dimension sign r'%(trademark)s', # trademark r'%(registered)s', # registered r'%(copyright)s', # copyright r'%(half)s', # 1/2 r'%(quarter)s', # 1/4 r'%(threequarters)s', # 3/4 r'%(degrees)s', # degrees r'%(plusminus)s', # plus/minus r'\1', # 3+ uppercase acronym r'\1\2', # 3+ uppercase )] if self.html_type == 'html5': self.glyph_replace[19] = r'\1' if self.restricted is True: self.url_schemes = self.restricted_url_schemes else: self.url_schemes = self.unrestricted_url_schemes def parse(self, text, rel=None, head_offset=0, sanitize=False): """ >>> import textile >>> Py3 << textile.textile('some textile') '\\t

some textile

' """ self.notes = OrderedDict() self.unreferencedNotes = OrderedDict() self.notelist_cache = OrderedDict() text = _normalize_newlines(text) if self.restricted: text = self.encode_html(text, quotes=False) if rel: self.rel = ' rel="%s"' % rel text = self.getRefs(text) # The original php puts the below within an if not self.lite, but our # block function handles self.lite itself. text = self.block(text, int(head_offset)) if not self.lite: text = self.placeNoteLists(text) text = self.retrieve(text) if sanitize: text = sanitizer.sanitize(text) # if the text contains a break tag (
or
) not followed by # a newline, replace it with a new style break tag and a newline. text = re.sub(r'(?!\n)', '
\n', text) return text def pba(self, block_attributes, element=None): """ Parse block attributes. >>> t = Textile() >>> Py3 << t.pba(r'\3') '' >>> Py3 << t.pba(r'\\3', element='td') ' colspan="3"' >>> Py3 << t.pba(r'/4', element='td') ' rowspan="4"' >>> Py3 << t.pba(r'\\3/4', element='td') ' colspan="3" rowspan="4"' >>> Py3 << t.pba('^', element='td') ' style="vertical-align:top;"' >>> Py3 << t.pba('{line-height:18px}') ' style="line-height:18px;"' >>> Py3 << t.pba('(foo-bar)') ' class="foo-bar"' >>> Py3 << t.pba('(#myid)') ' id="myid"' >>> Py3 << t.pba('(foo-bar#myid)') ' class="foo-bar" id="myid"' >>> Py3 << t.pba('((((') ' style="padding-left:4em;"' >>> Py3 << t.pba(')))') ' style="padding-right:3em;"' >>> Py3 << t.pba('[fr]') ' lang="fr"' >>> Py3 << t.pba(r'\\5 80', 'col') ' span="5" width="80"' >>> rt = Textile() >>> rt.restricted = True >>> Py3 << rt.pba('[en]') ' lang="en"' >>> Py3 << rt.pba('(#id)') '' """ style = [] aclass = '' lang = '' colspan = '' rowspan = '' block_id = '' span = '' width = '' if not block_attributes: return '' matched = block_attributes if element == 'td': m = re.search(r'\\(\d+)', matched) if m: colspan = m.group(1) m = re.search(r'/(\d+)', matched) if m: rowspan = m.group(1) if element == 'td' or element == 'tr': m = re.search(r'(%s)' % self.valign_re_s, matched) if m: style.append("vertical-align:%s" % self.vAlign[m.group(1)]) m = re.search(r'\{([^}]*)\}', matched) if m: style += m.group(1).rstrip(';').split(';') matched = matched.replace(m.group(0), '') m = re.search(r'\[([^\]]+)\]', matched, re.U) if m: lang = m.group(1) matched = matched.replace(m.group(0), '') m = re.search(r'\(([^()]+)\)', matched, re.U) if m: aclass = m.group(1) matched = matched.replace(m.group(0), '') m = re.search(r'([(]+)', matched) if m: style.append("padding-left:%sem" % len(m.group(1))) matched = matched.replace(m.group(0), '') m = re.search(r'([)]+)', matched) if m: style.append("padding-right:%sem" % len(m.group(1))) matched = matched.replace(m.group(0), '') m = re.search(r'(%s)' % self.halign_re_s, matched) if m: style.append("text-align:%s" % self.hAlign[m.group(1)]) m = re.search(r'^(.*)#(.*)$', aclass) if m: block_id = m.group(2) aclass = m.group(1) if element == 'col': pattern = r'(?:\\(\d+))?\s*(\d+)?' csp = re.match(pattern, matched) span, width = csp.groups() if self.restricted: if lang: return ' lang="%s"' % lang else: return '' result = [] if style: # Previous splits that created style may have introduced extra # whitespace into the list elements. Clean it up. style = [x.strip() for x in style] result.append(' style="%s;"' % "; ".join(style)) if aclass: result.append(' class="%s"' % aclass) if block_id: result.append(' id="%s"' % block_id) if lang: result.append(' lang="%s"' % lang) if colspan: result.append(' colspan="%s"' % colspan) if rowspan: result.append(' rowspan="%s"' % rowspan) if span: result.append(' span="%s"' % span) if width: result.append(' width="%s"' % width) return ''.join(result) def hasRawText(self, text): """ checks whether the text has text not already enclosed by a block tag >>> t = Textile() >>> t.hasRawText('

foo bar biz baz

') False >>> t.hasRawText(' why yes, yes it does') True """ # The php version has orders the below list of tags differently. The # important thing to note here is that the pre must occur before the # p or else the regex module doesn't properly match pre-s. It only # matches the p in pre. r = re.compile(r'<(pre|p|blockquote|div|form|table|ul|ol|dl|h[1-6])[^>]*?>.*', re.S).sub('', text.strip()).strip() r = re.compile(r'<(hr|br)[^>]*?/>').sub('', r) return '' != r def table(self, text): r""" >>> t = Textile() >>> Py3 << t.table('(rowclass). |one|two|three|\n|a|b|c|') '\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t
onetwothree
abc
\n\n' """ text = text + "\n\n" pattern = re.compile(r'^(?:table(_?%(s)s%(a)s%(c)s)\.(.*?)\n)?^(%(a)s%(c)s\.? ?\|.*\|)[\s]*\n\n' % {'s': self.table_span_re_s, 'a': self.align_re_s, 'c': self.cslh_re_s}, re.S | re.M | re.U) return pattern.sub(self.fTable, text) def fTable(self, match): tatts = self.pba(match.group(1), 'table') summary = (' summary="%s"' % match.group(2).strip() if match.group(2) else '') cap = '' colgrp, last_rgrp = '', '' c_row = 1 rows = [] try: split = re.split(r'\|\s*?$', match.group(3), flags=re.M) except TypeError: split = re.compile(r'\|\s*?$', re.M).split(match.group(3)) for row in [x for x in split if x]: row = row.lstrip() # Caption -- only occurs on row 1, otherwise treat '|=. foo |...' # as a normal center-aligned cell. captionpattern = r"^\|\=(%(s)s%(a)s%(c)s)\. ([^\n]*)(.*)" % {'s': self.table_span_re_s, 'a': self.align_re_s, 'c': self.cslh_re_s} caption_re = re.compile(captionpattern, re.S) cmtch = caption_re.match(row) if c_row == 1 and cmtch: capatts = self.pba(cmtch.group(1)) cap = "\t%s\n" % (capatts, cmtch.group(2).strip()) row = cmtch.group(3).lstrip() if row == '': continue c_row += 1 # Colgroup grppattern = r"^\|:(%(s)s%(a)s%(c)s\. .*)" % {'s': self.table_span_re_s, 'a': self.align_re_s, 'c': self.cslh_re_s} grp_re = re.compile(grppattern, re.M) gmtch = grp_re.match(row.lstrip()) if gmtch: has_newline = "\n" in row idx = 0 for col in gmtch.group(1).replace('.', '').split("|"): gatts = self.pba(col.strip(), 'col') if idx == 0: gatts = "group%s>" % gatts else: gatts = gatts + " />" colgrp = colgrp + "\t 0: ctag = "t%s" % ctyp cline = ("\t\t\t<%(ctag)s%(catts)s>%(cell)s" % {'ctag': ctag, 'catts': catts, 'cell': cell}) cells.append(self.doTagBr(ctag, cline)) cellctr += 1 if rgrp and last_rgrp: grp = "\t\n" % last_rgrp else: grp = '' if rgrp: grp += "\t\n" % (rgrp, rgrpatts) last_rgrp = rgrp if rgrp else last_rgrp rows.append("%s\t\t\n%s%s\t\t" % (grp, ratts, '\n'.join(cells), '\n' if cells else '')) cells = [] catts = None if last_rgrp: last_rgrp = '\t\n' % last_rgrp tbl = ("\t\n%(cap)s%(colgrp)s%(rows)s\n%(last_rgrp)s\t\n\n" % {'tatts': tatts, 'summary': summary, 'cap': cap, 'colgrp': colgrp, 'last_rgrp': last_rgrp, 'rows': '\n'.join(rows)}) return tbl def lists(self, text): """ >>> t = Textile() >>> Py3 << t.lists("* one\\n* two\\n* three") '\\t
    \\n\\t\\t
  • one
  • \\n\\t\\t
  • two
  • \\n\\t\\t
  • three
  • \\n\\t
' """ #Replace line-initial bullets with asterisks bullet_pattern = re.compile('^•', re.U | re.M) pattern = re.compile(r'^((?:[*;:]+|[*;:#]*#(?:_|\d+)?)%s[ .].*)$(?![^#*;:])' % self.csl_re_s, re.U | re.M | re.S) return pattern.sub(self.fList, bullet_pattern.sub('*', text)) def fList(self, match): try: text = re.split(r'\n(?=[*#;:])', match.group(), flags=re.M) except TypeError: text = re.compile(r'\n(?=[*#;:])', re.M).split(match.group()) pt = '' result = [] ls = OrderedDict() for i, line in enumerate(text): try: nextline = text[i + 1] except IndexError: nextline = '' m = re.search(r"^([#*;:]+)(_|\d+)?(%s)[ .](.*)$" % self.csl_re_s, line, re.S) if m: tl, start, atts, content = m.groups() content = content.strip() nl = '' ltype = self.listType(tl) if ';' in tl: litem = 'dt' elif ':' in tl: litem = 'dd' else: litem = 'li' showitem = len(content) > 0 # handle list continuation/start attribute on ordered lists if ltype == 'o': if not hasattr(self, 'olstarts'): self.olstarts = {tl: 1} # does the first line of this ol have a start attribute if len(tl) > len(pt): # no, set it to 1 if start is None: self.olstarts[tl] = 1 # yes, set it to the given number elif start != '_': self.olstarts[tl] = int(start) # we won't need to handle the '_' case, we'll just # print out the number when it's needed # put together the start attribute if needed if len(tl) > len(pt) and start is not None: start = ' start="%s"' % self.olstarts[tl] # This will only increment the count for list items, not # definition items if showitem: self.olstarts[tl] += 1 nm = re.match("^([#\*;:]+)(_|[\d]+)?%s[ .].*" % self.csl_re_s, nextline) if nm: nl = nm.group(1) # We need to handle nested definition lists differently. If # the next tag is a dt (';') of a lower nested level than the # current dd (':'), if ';' in pt and ':' in tl: ls[tl] = 2 atts = self.pba(atts) # If start is still None, set it to '', else leave the value # that we've already formatted. start = start or '' # if this item tag isn't in the list, create a new list and # item, else just create the item if tl not in ls: ls[tl] = 1 itemtag = ("\n\t\t<%s>%s" % (litem, content) if showitem else '') line = "\t<%sl%s%s>%s" % (ltype, atts, start, itemtag) else: line = ("\t\t<%s%s>%s" % (litem, atts, content) if showitem else '') if len(nl) <= len(tl): line = line + ("" % litem if showitem else '') # work backward through the list closing nested lists/items for k, v in reversed(list(ls.items())): if len(k) > len(nl): if v != 2: line = line + "\n\t" % self.listType(k) if len(k) > 1 and v != 2: line = line + "" % litem del ls[k] # Remember the current Textile tag pt = tl # This else exists in the original php version. I'm not sure how # to come up with a case where the line would not match. I think # it may have been necessary due to the way php returns matches. #else: #line = line + "\n" result.append(line) return self.doTagBr(litem, "\n".join(result)) def listType(self, list_string): listtypes = { list_string.startswith('*'): 'u', list_string.startswith('#'): 'o', (not list_string.startswith('*') and not list_string.startswith('#')): 'd' } return listtypes[True] def doTagBr(self, tag, input): return re.compile(r'<(%s)([^>]*?)>(.*)()' % re.escape(tag), re.S).sub(self.doBr, input) def doPBr(self, in_): return re.compile(r'<(p)([^>]*?)>(.*)()', re.S).sub(self.doBr, in_) def doBr(self, match): content = re.sub(r'(.+)(?:(?)|(?))\n(?![#*;:\s|])', r'\1
', match.group(3)) return '<%s%s>%s%s' % (match.group(1), match.group(2), content, match.group(4)) def block(self, text, head_offset=0): """ >>> t = Textile() >>> Py3 << t.block('h1. foobar baby') '\\t

foobar baby

' """ if not self.lite: tre = '|'.join(self.btag) else: tre = '|'.join(self.btag_lite) text = text.split('\n\n') tag = 'p' atts = cite = graf = ext = '' c1 = '' out = [] anon = False for line in text: pattern = r'^(%s)(%s%s)\.(\.?)(?::(\S+))? (.*)$' % ( tre, self.align_re_s, self.cslh_re_s ) match = re.search(pattern, line, re.S) if match: if ext: out.append(out.pop() + c1) tag, atts, ext, cite, graf = match.groups() h_match = re.search(r'h([1-6])', tag) if h_match: head_level, = h_match.groups() tag = 'h%i' % max(1, min(int(head_level) + head_offset, 6)) o1, o2, content, c2, c1, eat = self.fBlock(tag, atts, ext, cite, graf) # leave off c1 if this block is extended, # we'll close it at the start of the next block if ext: line = "%s%s%s%s" % (o1, o2, content, c2) else: line = "%s%s%s%s%s" % (o1, o2, content, c2, c1) else: anon = True if ext or not re.search(r'^\s', line): o1, o2, content, c2, c1, eat = self.fBlock(tag, atts, ext, cite, line) # skip $o1/$c1 because this is part of a continuing # extended block if tag == 'p' and not self.hasRawText(content): line = content else: line = "%s%s%s" % (o2, content, c2) else: line = self.graf(line) line = self.doPBr(line) line = re.sub(r'
', '
', line) if ext and anon: out.append(out.pop() + "\n" + line) elif not eat: out.append(line) if not ext: tag = 'p' atts = '' cite = '' graf = '' if ext: out.append(out.pop() + c1) return '\n\n'.join(out) def fBlock(self, tag, atts, ext, cite, content): """ >>> t = Textile() >>> Py3 << t.fBlock("bq", "", None, "", "Hello BlockQuote") ('\\t
\\n', '\\t\\t

', 'Hello BlockQuote', '

', '\\n\\t
', False) >>> Py3 << t.fBlock("bq", "", None, "http://google.com", "Hello BlockQuote") ('\\t
\\n', '\\t\\t

', 'Hello BlockQuote', '

', '\\n\\t
', False) >>> Py3 << t.fBlock("bc", "", None, "", 'printf "Hello, World";') # doctest: +ELLIPSIS ('
', '', ..., '', '
', False) >>> Py3 << t.fBlock("h1", "", None, "", "foobar") ('', '\\t

', 'foobar', '

', '', False) """ att = atts atts = self.pba(atts) o1 = o2 = c2 = c1 = '' eat = False if tag == 'p': # is this an anonymous block with a note definition? notedef_re = re.compile(r""" ^note\# # start of note def marker ([^%%<*!@#^([{ \s.]+) # !label ([*!^]?) # !link (%s) # !att \.? # optional period. [\s]+ # whitespace ends def marker (.*)$ # !content""" % (self.cslh_re_s), re.X) notedef = notedef_re.sub(self.fParseNoteDefs, content) # It will be empty if the regex matched and ate it. if '' == notedef: return o1, o2, notedef, c2, c1, True m = re.search(r'fn(\d+)', tag) if m: tag = 'p' if m.group(1) in self.fn: fnid = self.fn[m.group(1)] else: fnid = m.group(1) # If there is an author-specified ID goes on the wrapper & the # auto-id gets pushed to the supp_id = '' # if class has not been previously specified, set it to "footnote" if atts.find('class=') < 0: atts = atts + ' class="footnote"' # if there's no specified id, use the generated one. if atts.find('id=') < 0: atts = atts + ' id="fn%s"' % fnid else: supp_id = ' id="fn%s"' % fnid if att.find('^') < 0: sup = self.formatFootnote(m.group(1), supp_id) else: fnrev = '%s' % (fnid, m.group(1)) sup = self.formatFootnote(fnrev, supp_id) content = sup + ' ' + content if tag == 'bq': cite = self.checkRefs(cite) if cite: cite = ' cite="%s"' % cite else: cite = '' o1 = "\t\n" % (cite, atts) o2 = "\t\t" % atts c2 = "

" c1 = "\n\t" elif tag == 'bc': o1 = "" % atts o2 = "" % atts c2 = "" c1 = "" content = self.shelve(self.encode_html(content.rstrip("\n") + "\n")) elif tag == 'notextile': content = self.shelve(content) o1 = o2 = '' c1 = c2 = '' elif tag == 'pre': content = self.shelve(self.encode_html(content.rstrip("\n") + "\n")) o1 = "" % atts o2 = c2 = '' c1 = '' elif tag == '###': eat = True else: o2 = "\t<%s%s>" % (tag, atts) c2 = "" % tag if not eat: content = self.graf(content) else: content = '' return o1, o2, content, c2, c1, eat def formatFootnote(self, marker, atts='', anchor=True): if anchor: pattern = self.glyph_definitions['fn_foot_pattern'] else: pattern = self.glyph_definitions['fn_ref_pattern'] return pattern % {'atts': atts, 'marker': marker} def footnoteRef(self, text): """ >>> t = Textile() >>> Py3 << t.footnoteRef('foo[1] ') # doctest: +ELLIPSIS 'foo1 ' """ return re.compile(r'(?<=\S)\[(\d+)(!?)\](\s)?', re.U).sub( self.footnoteID, text ) def footnoteID(self, match): footnoteNum, nolink, space = match.groups() if not space: space = '' backref = ' class="footnote"' if footnoteNum not in self.fn: a = uuid.uuid4().hex self.fn[footnoteNum] = a backref = '%s id="fnrev%s"' % (backref, a) footnoteID = self.fn[footnoteNum] footref = '!' == nolink and footnoteNum or '%s' % ( footnoteID, footnoteNum ) footref = self.formatFootnote(footref, backref, False) return footref + space def glyphs(self, text): """ Because of the split command, the regular expressions are different for when the text at the beginning and the rest of the text. for example: let's say the raw text provided is "*Here*'s some textile" before it gets to this glyphs method, the text has been converted to "Here's some textile" When run through the split, we end up with ["", "Here", "", "'s some textile"]. The re.search that follows tells it not to ignore html tags. If the single quote is the first character on the line, it's an open single quote. If it's the first character of one of those splits, it's an apostrophe or closed single quote, but the regex will bear that out. A similar situation occurs for double quotes as well. So, for the first pass, we use the glyph_search_initial set of regexes. For all remaining passes, we use glyph_search >>> t = Textile() >>> Py3 << t.glyphs("apostrophe's") 'apostrophe’s' >>> Py3 << t.glyphs("back in '88") 'back in ’88' >>> Py3 << t.glyphs('foo ...') 'foo …' >>> Py3 << t.glyphs('--') '—' >>> Py3 << t.glyphs('FooBar[tm]') 'FooBar™' >>> Py3 << t.glyphs("

Cat's Cradle by Vonnegut

") '

Cat’s Cradle by Vonnegut

' """ # fix: hackish text = re.sub(r'"\Z', r'" ', text) result = [] searchlist = self.glyph_search_initial for i, line in enumerate(re.compile(r'(<[\w\/!?].*?>)', re.U).split(text)): if not i % 2: for s, r in zip(searchlist, self.glyph_replace): line = s.sub(r, line) result.append(line) if i == 0: searchlist = self.glyph_search return ''.join(result) def getRefs(self, text): """ Capture and store URL references in self.urlrefs. >>> t = Textile() >>> Py3 << t.getRefs("some text [Google]http://www.google.com") 'some text ' >>> Py3 << t.urlrefs {'Google': 'http://www.google.com'} """ pattern = re.compile(r'(?:(?<=^)|(?<=\s))\[(.+)\]((?:http(?:s?):\/\/|\/)\S+)(?=\s|$)', re.U) text = pattern.sub(self.refs, text) return text def refs(self, match): flag, url = match.groups() self.urlrefs[flag] = url return '' def checkRefs(self, url): return self.urlrefs.get(url, url) def isRelURL(self, url): """ Identify relative urls. >>> t = Textile() >>> t.isRelURL("http://www.google.com/") False >>> t.isRelURL("/foo") True """ (scheme, netloc) = urlparse(url)[0:2] return not scheme and not netloc def relURL(self, url): """ >>> t = Textile() >>> Py3 << t.relURL("http://www.google.com/") 'http://www.google.com/' >>> t.restricted = True >>> Py3 << t.relURL("gopher://gopher.com/") '#' """ scheme = urlparse(url)[0] if scheme and scheme not in self.url_schemes: return '#' return url def shelve(self, text): itemID = uuid.uuid4().hex self.shelf[itemID] = text return itemID def retrieve(self, text): """ >>> t = Textile() >>> id = t.shelve("foobar") >>> Py3 << t.retrieve(id) 'foobar' """ while True: old = text for k, v in self.shelf.items(): text = text.replace(k, v) if text == old: break return text def encode_html(self, text, quotes=True): """Return text that's safe for an HTML attribute. >>> t = Textile() >>> Py3 << t.encode_html('this is a "test" of text that\\\'s safe to put in an attribute.') 'this is a "test" of text that's safe to put in an <html> attribute.' """ a = ( ('&', '&'), ('<', '<'), ('>', '>')) if quotes: a = a + (("'", '''), ('"', '"')) for k, v in a: text = text.replace(k, v) return text def graf(self, text): if not self.lite: text = self.noTextile(text) text = self.code(text) text = self.getHTMLComments(text) text = self.getRefs(text) text = self.links(text) if self.auto_link: text = self.autoLink(text) text = self.links(text) if not self.noimage: text = self.image(text) if not self.lite: text = self.table(text) text = self.redcloth_list(text) text = self.lists(text) text = self.span(text) text = self.footnoteRef(text) text = self.noteRef(text) text = self.glyphs(text) return text.rstrip('\n') def autoLink(self, text): """ >>> t = Textile() >>> Py3 << t.autoLink("http://www.ya.ru") '"$":http://www.ya.ru' """ pattern = re.compile(r"""\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""", re.U | re.I) return pattern.sub(r'"$":\1', text) def links(self, text): """ >>> t = Textile() >>> Py3 << t.links('fooobar "Google":http://google.com/foobar/ and hello world "flickr":http://flickr.com/photos/jsamsa/ ') # doctest: +ELLIPSIS 'fooobar ... and hello world ...' """ # For some reason, the part of the regex below that matches the url # does not match a trailing parenthesis. It gets caught by tail, and # we check later to see if it should be included as part of the url. pattern = r''' (?P
^|(?<=[\s>.\(\|])|[{[])?    # leading text
            "                                   # opening quote
            (?P%s)                        # block attributes
            (?P[^"]+?)                    # link text
            \s?                                 # optional space
            (?:\((?P[^)]+?)\)(?="))?     # optional title
            ":                                  # closing quote, colon
            (?P<url>%s+?)                       # URL
            (?P<slash>\/)?                      # slash
            (?P<post>[^\w\/]*?)                 # trailing text
            (?P<tail>[\]})]|(?=\s|$|\|))        # tail
        ''' % (self.cslh_re_s, self.urlchar_re_s)

        text = re.compile(pattern, re.X | re.U).sub(self.fLink, text)

        return text

    def fLink(self, match):
        pre, atts, text, title, url, slash, post, tail = match.groups()

        if not pre:
            pre = ''

        if not slash:
            slash = ''

        if text == '$':
            text = re.sub(r'^\w+://(.+)', r'\1', url)

        # assume ) at the end of the url is not actually part of the url
        # unless the url also contains a (
        if tail == ')' and url.find('(') > -1:
            url = url + tail
            tail = None

        url = self.checkRefs(url)
        try:
            url = self.encode_url(url)
        except:
            pass

        atts = self.pba(atts)
        if title:
            atts = atts + ' title="%s"' % self.encode_html(title)

        if not self.noimage:
            text = self.image(text)

        text = self.span(text)
        text = self.glyphs(text)

        url = self.relURL(url) + slash
        out = '<a href="%s"%s%s>%s</a>' % (self.encode_html(url), atts,
                                           self.rel, text)

        if (pre and not tail) or (tail and not pre):
            out = ''.join([pre, out, post, tail])
            post = ''

        out = self.shelve(out)
        return ''.join([out, post])

    def encode_url(self, url):
        """
        Converts a (unicode) URL to an ASCII URL, with the domain part
        IDNA-encoded and the path part %-encoded (as per RFC 3986).

        Fixed version of the following code fragment from Stack Overflow:
            http://stackoverflow.com/a/804380/72656
        """
        # turn string into unicode
        if not isinstance(url, unicode):
            url = url.decode('utf8')

        # parse it
        parsed = urlsplit(url)

        # divide the netloc further
        netloc_pattern = re.compile(r"""
            (?:(?P<user>[^:@]+)(?::(?P<password>[^:@]+))?@)?
            (?P<host>[^:]+)
            (?::(?P<port>[0-9]+))?
        """, re.X | re.U)
        netloc_parsed = netloc_pattern.match(parsed.netloc).groupdict()

        # encode each component
        scheme = parsed.scheme
        user = netloc_parsed['user'] and quote(netloc_parsed['user'])
        password = (netloc_parsed['password'] and
                    quote(netloc_parsed['password']))
        host = netloc_parsed['host']
        port = netloc_parsed['port'] and netloc_parsed['port']
        path = '/'.join(  # could be encoded slashes!
            quote(unquote(pce).encode('utf8'), b'')
            for pce in parsed.path.split('/')
        )
        query = quote(unquote(parsed.query), b'=&?/')
        fragment = quote(unquote(parsed.fragment))

        # put it back together
        netloc = ''
        if user:
            netloc += user
            if password:
                netloc += ':' + password
            netloc += '@'
        netloc += host
        if port:
            netloc += ':'+port
        return urlunsplit((scheme, netloc, path, query, fragment))

    def span(self, text):
        """
        >>> t = Textile()
        >>> Py3 << t.span(r"hello %(bob)span *strong* and **bold**% goodbye")
        'hello <span class="bob">span <strong>strong</strong> and <b>bold</b></span> goodbye'
        """
        qtags = (r'\*\*', r'\*', r'\?\?', r'\-', r'__',
                 r'_', r'%', r'\+', r'~', r'\^')
        pnct = ".,\"'?!;:("

        for qtag in qtags:
            pattern = re.compile(r"""
                (?:^|(?<=[\s>%(pnct)s])|([\[{]))
                (%(qtag)s)(?!%(qtag)s)
                (%(c)s)
                (?::\(([^)]+?)\))?
                ([^\s%(qtag)s]+|\S[^%(qtag)s\n]*[^\s%(qtag)s\n])
                ([%(pnct)s]*)
                %(qtag)s
                (?:$|([\]}])|(?=%(selfpnct)s{1,2}|\s))
            """ % {'qtag': qtag, 'c': self.cslh_re_s, 'pnct': pnct,
                   'selfpnct': self.pnct_re_s}, re.X)
            text = pattern.sub(self.fSpan, text)
        return text

    def fSpan(self, match):
        _, tag, atts, cite, content, end, _ = match.groups()

        qtags = {
            '*':  'strong',
            '**': 'b',
            '??': 'cite',
            '_':  'em',
            '__': 'i',
            '-':  'del',
            '%':  'span',
            '+':  'ins',
            '~':  'sub',
            '^':  'sup'
        }

        tag = qtags[tag]
        atts = self.pba(atts)
        if cite:
            atts = atts + ' cite="%s"' % cite

        content = self.span(content)

        out = "<%s%s>%s%s</%s>" % (tag, atts, content, end, tag)
        return out

    def image(self, text):
        """
        >>> t = Textile()
        >>> Py3 << t.image('!/imgs/myphoto.jpg!:http://jsamsa.com')
        '<a href="http://jsamsa.com" class="img"><img alt="" src="/imgs/myphoto.jpg" /></a>'
        >>> Py3 << t.image('!</imgs/myphoto.jpg!')
        '<img align="left" alt="" src="/imgs/myphoto.jpg" />'
        """
        pattern = re.compile(r"""
            (?:[\[{])?         # pre
            \!                 # opening !
            (\<|\=|\>)?        # optional alignment atts
            (%s)               # optional style,class atts
            (?:\. )?           # optional dot-space
            ([^\s(!]+)         # presume this is the src
            \s?                # optional space
            (?:\(([^\)]+)\))?  # optional title
            \!                 # closing
            (?::(\S+))?        # optional href
            (?:[\]}]|(?=\s|$)) # lookahead: space or end of string
        """ % self.cslh_re_s, re.U | re.X)
        return pattern.sub(self.fImage, text)

    def fImage(self, match):
        # (None, '', '/imgs/myphoto.jpg', None, None)
        align, atts, url, title, href = match.groups()
        atts = self.pba(atts)
        size = None

        alignments = {'<': 'left', '=': 'center', '>': 'right'}

        if not title:
            title = ''

        if not self.isRelURL(url) and self.get_sizes:
            size = imagesize.getimagesize(url)

        if href:
            href = self.checkRefs(href)

        url = self.checkRefs(url)
        url = self.relURL(url)

        out = []
        if href:
            out.append('<a href="%s" class="img">' % href)
        out.append('<img')
        if align:
            out.append(' align="%s"' % alignments[align])
        out.append(' alt="%s"' % title)
        if size:
            out.append(' height="%s"' % size[1])
        out.append(' src="%s"' % url)
        if atts:
            out.append(atts)
        if title:
            out.append(' title="%s"' % title)
        if size:
            out.append(' width="%s"' % size[0])
        out.append(' />')
        if href:
            out.append('</a>')

        return ''.join(out)

    def code(self, text):
        text = self.doSpecial(text, '<code>', '</code>', self.fCode)
        text = self.doSpecial(text, '@', '@', self.fCode)
        text = self.doSpecial(text, '<pre>', '</pre>', self.fPre)
        return text

    def fCode(self, match):
        before, text, after = match.groups()
        if after is None:
            after = ''
        # text needs to be escaped
        if not self.restricted:
            text = self.encode_html(text, quotes=False)
        return ''.join([before, self.shelve('<code>%s</code>' % text), after])

    def fPre(self, match):
        before, text, after = match.groups()
        if after is None:
            after = ''
        # text needs to be escaped
        if not self.restricted:
            text = self.encode_html(text)
        return ''.join([before, '<pre>', self.shelve(text), '</pre>', after])

    def doSpecial(self, text, start, end, method):
        pattern = re.compile(r'(^|\s|[\[({>|])%s(.*?)%s($|[\])}])?'
                             % (re.escape(start), re.escape(end)), re.M | re.S)
        return pattern.sub(method, text)

    def noTextile(self, text):
        text = self.doSpecial(text, '<notextile>', '</notextile>',
                              self.fTextile)
        return self.doSpecial(text, '==', '==', self.fTextile)

    def fTextile(self, match):
        before, notextile, after = match.groups()
        if after is None:
            after = ''
        return ''.join([before, self.shelve(notextile), after])

    def getHTMLComments(self, text):
        """Search the string for HTML comments, e.g. <!-- comment text -->.  We
        send the text that matches this to fParseHTMLComments."""
        return self.doSpecial(text, '<!--', '-->', self.fParseHTMLComments)

    def fParseHTMLComments(self, match):
        """If self.restricted is True, clean the matched contents of the HTML
        comment.  Otherwise, return the comments unchanged.
        The original php had an if statement in here regarding restricted mode.
        nose reported that this line wasn't covered.  It's correct.  In
        restricted mode, the html comment tags have already been converted to
        <!*#8212; and —> so they don't match in getHTMLComments,
        and never arrive here.
        """
        before, commenttext, after = match.groups()
        commenttext = self.shelve(commenttext)
        return '<!--%s-->' % commenttext

    def redcloth_list(self, text):
        """Parse the text for definition lists and send them to be
        formatted."""
        pattern = re.compile(r"^([-]+%s[ .].*:=.*)$(?![^-])" % self.csl_re_s,
                             re.M | re.U | re.S)
        return pattern.sub(self.fRCList, text)

    def fRCList(self, match):
        """Format a definition list."""
        out = []
        try:
            text = re.split(r'\n(?=[-])', match.group(), flags=re.M)
        except TypeError:
            text = re.compile(r'\n(?=[-])', re.M).split(match.group())
        for line in text:
            # parse the attributes and content
            m = re.match(r'^[-]+(%s)[ .](.*)$' % self.csl_re_s, line, re.M |
                    re.S)

            atts, content = m.groups()
            # cleanup
            content = content.strip()
            atts = self.pba(atts)

            # split the content into the term and definition
            xm = re.match(r'^(.*?)[\s]*:=(.*?)[\s]*(=:|:=)?[\s]*$', content,
                          re.S)
            term, definition, ending = xm.groups()
            # cleanup
            term = term.strip()
            definition = definition.strip(' ')

            # if this is the first time through, out as a bool is False
            if not out:
                if definition == '':
                    dltag = "<dl%s>" % atts
                else:
                    dltag = "<dl>"
                out.append(dltag)

            if definition != '' and term != '':
                if definition.startswith('\n'):
                    definition = '<p>%s</p>' % definition.lstrip()
                definition = definition.replace('\n', '<br />').strip()

                term = self.graf(term)
                definition = self.graf(definition)

                out.extend(['\t<dt%s>%s</dt>' % (atts, term), '\t<dd>%s</dd>' %
                           definition])

        out.append('</dl>')
        out = '\n'.join(out)
        return out

    def placeNoteLists(self, text):
        """Parse the text for endnotes."""
        if self.notes:
            o = OrderedDict()
            for label, info in self.notes.items():
                if 'seq' in info:
                    i = info['seq']
                    info['seq'] = label
                    o[i] = info
                else:
                    self.unreferencedNotes[label] = info

            if o:
                # sort o by key
                o = OrderedDict(sorted(o.items(), key=lambda t: t[0]))
            self.notes = o
        text_re = re.compile('<p>notelist(%s)(?:\:([\w|%s]))?([\^!]?)(\+?)\.?[\s]*</p>'
                             % (self.cslh_re_s, self.syms_re_s), re.U)
        text = text_re.sub(self.fNoteLists, text)
        return text

    def fNoteLists(self, match):
        """Given the text that matches as a note, format it into HTML."""
        att, start_char, g_links, extras = match.groups()
        start_char = start_char or 'a'
        index = '%s%s%s' % (g_links, extras, start_char)
        result = ''

        if index not in self.notelist_cache:
            o = []
            if self.notes:
                for seq, info in self.notes.items():
                    links = self.makeBackrefLink(info, g_links, start_char)
                    atts = ''
                    if 'def' in info:
                        infoid = info['id']
                        atts = info['def']['atts']
                        content = info['def']['content']
                        li = ("""\t<li%s>%s<span id="note%s"> </span>%s</li>"""
                              % (atts, links, infoid, content))
                    else:
                        li = ("""\t<li%s>%s Undefined Note [#%s].<li>""" %
                              (atts, links, info['seq']))
                    o.append(li)
            if '+' == extras and self.unreferencedNotes:
                for seq, info in self.unreferencedNotes.items():
                    if info['def']:
                        atts = info['def']['atts']
                        content = info['def']['content']
                        li = """\t<li%s>%s</li>""" % (atts, content)
                    o.append(li)
            self.notelist_cache[index] = "\n".join(o)
            result = self.notelist_cache[index]
        if result:
            list_atts = self.pba(att)
            result = """<ol%s>\n%s\n</ol>""" % (list_atts, result)
        return result

    def makeBackrefLink(self, info, g_links, i):
        """Given the pieces of a back reference link, create an <a> tag."""
        atts, content, infoid, link = '', '', '', ''
        if 'def' in info:
            link = info['def']['link']
        backlink_type = link or g_links
        i_ = self.encode_high(i)
        allow_inc = i not in self.syms_re_s
        i_ = int(i_)

        if backlink_type == "!":
            return ''
        elif backlink_type == '^':
            return """<sup><a href="#noteref%s">%s</a></sup>""" % (
                info['refids'][0], i)
        else:
            result = []
            for refid in info['refids']:
                i_entity = self.decode_high(i_)
                sup = """<sup><a href="#noteref%s">%s</a></sup>""" % (refid,
                        i_entity)
                if allow_inc:
                    i_ += 1
                result.append(sup)
            result = ' '.join(result)
            return result

    def fParseNoteDefs(self, m):
        """Parse the note definitions and format them as HTML"""
        label, link, att, content = m.groups()

        # Assign an id if the note reference parse hasn't found the label yet.
        if label not in self.notes:
            self.notes[label] = {'id': uuid.uuid4().hex}

        # Ignores subsequent defs using the same label
        if 'def' not in self.notes[label]:
            self.notes[label]['def'] = {'atts': self.pba(att), 'content':
                                        self.graf(content), 'link': link}
        return ''

    def noteRef(self, text):
        """Search the text looking for note references."""
        text_re = re.compile(r"""
        \[          # start
        (%s)        # !atts
        \#
        ([^\]!]+)  # !label
        ([!]?)      # !nolink
        \]""" % self.cslh_re_s, re.X)
        text = text_re.sub(self.fParseNoteRefs, text)
        return text

    def fParseNoteRefs(self, match):
        """Parse and format the matched text into note references.
        By the time this function is called, all the defs will have been
        processed into the notes array. So now we can resolve the link numbers
        in the order we process the refs..."""
        atts, label, nolink = match.groups()
        atts = self.pba(atts)
        nolink = nolink == '!'

        # Assign a sequence number to this reference if there isn't one already
        if label in self.notes:
            num = self.notes[label]['seq']
        else:
            self.notes[label] = {
                'seq': self.note_index, 'refids': [], 'id': ''
            }
            num = self.note_index
            self.note_index += 1

        # Make our anchor point and stash it for possible use in backlinks when
        # the note list is generated later...
        refid = uuid.uuid4().hex
        self.notes[label]['refids'].append(refid)

        # If we are referencing a note that hasn't had the definition parsed
        # yet, then assign it an ID...
        if not self.notes[label]['id']:
            self.notes[label]['id'] = uuid.uuid4().hex
        labelid = self.notes[label]['id']

        # Build the link (if any)...
        result = '<span id="noteref%s">%s</span>' % (refid, num)
        if not nolink:
            result = """<a href="#note%s">%s</a>""" % (labelid, result)

        # Build the reference...
        result = '<sup%s>%s</sup>' % (atts, result)
        return result

    def encode_high(self, text):
        """Encode the text so that it is an appropriate HTML entity."""
        return ord(text)

    def decode_high(self, text):
        """Decode encoded HTML entities."""
        h = HTMLParser()
        text = '&#%s;' % text
        return h.unescape(text)


def textile(text, head_offset=0, html_type='xhtml', auto_link=False,
            encoding=None, output=None):
    """
    Apply Textile to a block of text.

    This function takes the following additional parameters:

    auto_link - enable automatic linking of URLs (default: False)
    head_offset - offset to apply to heading levels (default: 0)
    html_type - 'xhtml' or 'html5' style tags (default: 'xhtml')

    """
    return Textile(auto_link=auto_link, html_type=html_type).parse(text,
            head_offset=head_offset)


def textile_restricted(text, lite=True, noimage=True, html_type='xhtml',
                       auto_link=False):
    """
    Apply Textile to a block of text, with restrictions designed for weblog
    comments and other untrusted input.  Raw HTML is escaped, style attributes
    are disabled, and rel='nofollow' is added to external links.

    This function takes the following additional parameters:

    auto_link - enable automatic linking of URLs (default: False)
    html_type - 'xhtml' or 'html5' style tags (default: 'xhtml')
    lite - restrict block tags to p, bq, and bc, disable tables (default: True)
    noimage - disable image tags (default: True)

    """
    return Textile(restricted=True, lite=lite, noimage=noimage,
            auto_link=auto_link, html_type=html_type).parse( text,
                    rel='nofollow')


def setup_module(mod):
    """Inject Py3 to builtins for doctests."""
    try:
        import builtins
    except ImportError:
        import __builtin__ as builtins
    from textile.tools.doctest_utils import Py3
    builtins.Py3 = Py3
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/tests/������������������������������������������������������������������������0000755�0000767�0000024�00000000000�12462765643�016437� 5����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/tests/__init__.py�������������������������������������������������������������0000644�0000767�0000024�00000152105�12462764257�020554� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import textile
import re
from nose.tools import eq_, assert_true
from nose.plugins.skip import SkipTest

"""
('>>> import textile')
'<p>>>> import textile</p>'

"""


class TestKnownValues():
    xhtml_known_values = (
        ('hello, world', '\t<p>hello, world</p>'),

        ('A single paragraph.\n\nFollowed by another.',
         '\t<p>A single paragraph.</p>\n\n\t<p>Followed by another.</p>'),

        ('I am <b>very</b> serious.\n\n<pre>\nI am <b>very</b> serious.\n</pre>',
         '\t<p>I am <b>very</b> serious.</p>\n\n<pre>\nI am <b>very</b> serious.\n</pre>'),

        ('I spoke.\nAnd none replied.', '\t<p>I spoke.<br />\nAnd none replied.</p>'),

        ('"Observe!"', '\t<p>“Observe!” </p>'),

        ('Observe -- very nice!', '\t<p>Observe — very nice!</p>'),

        ('Observe - tiny and brief.', '\t<p>Observe – tiny and brief.</p>'),

        ('Observe...', '\t<p>Observe…</p>'),

        ('Observe ...', '\t<p>Observe …</p>'),

        ('Observe: 2 x 2.', '\t<p>Observe: 2 × 2.</p>'),

        ('one(TM), two(R), three(C).', '\t<p>one™, two®, three©.</p>'),

        ('h1. Header 1', '\t<h1>Header 1</h1>'),

        ('h2. Header 2', '\t<h2>Header 2</h2>'),

        ('h3. Header 3', '\t<h3>Header 3</h3>'),

        ('An old text\n\nbq. A block quotation.\n\nAny old text''',
        '\t<p>An old text</p>\n\n\t<blockquote>\n\t\t<p>A block quotation.</p>\n\t</blockquote>\n\n\t<p>Any old text</p>'),

        ('I _believe_ every word.', '\t<p>I <em>believe</em> every word.</p>'),

        ('And then? She *fell*!', '\t<p>And then? She <strong>fell</strong>!</p>'),

        ('I __know__.\nI **really** __know__.', '\t<p>I <i>know</i>.<br />\nI <b>really</b> <i>know</i>.</p>'),

        ("??Cat's Cradle?? by Vonnegut", '\t<p><cite>Cat’s Cradle</cite> by Vonnegut</p>'),

        ('Convert with @str(foo)@', '\t<p>Convert with <code>str(foo)</code></p>'),

        ('I\'m -sure- not sure.', '\t<p>I’m <del>sure</del> not sure.</p>'),

        ('You are a +pleasant+ child.', '\t<p>You are a <ins>pleasant</ins> child.</p>'),

        ('a ^2^ + b ^2^ = c ^2^', '\t<p>a <sup>2</sup> + b <sup>2</sup> = c <sup>2</sup></p>'),

        ('log ~2~ x', '\t<p>log <sub>2</sub> x</p>'),

        ('I\'m %unaware% of most soft drinks.', '\t<p>I’m <span>unaware</span> of most soft drinks.</p>'),

        ("I'm %{color:red}unaware%\nof most soft drinks.", '\t<p>I’m <span style="color:red;">unaware</span><br />\nof most soft drinks.</p>'),

        ('p(example1). An example', '\t<p class="example1">An example</p>'),

        ('p(#big-red). Red here', '\t<p id="big-red">Red here</p>'),

        ('p(example1#big-red2). Red here', '\t<p class="example1" id="big-red2">Red here</p>'),

        ('p{color:blue;margin:30px}. Spacey blue', '\t<p style="color:blue; margin:30px;">Spacey blue</p>'),

        ('p[fr]. rouge', '\t<p lang="fr">rouge</p>'),

        ('I seriously *{color:red}blushed*\nwhen I _(big)sprouted_ that\ncorn stalk from my\n%[es]cabeza%.',
        '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>'
        ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'),

        ('p<. align left', '\t<p style="text-align:left;">align left</p>'),

        ('p>. align right', '\t<p style="text-align:right;">align right</p>'),

        ('p=. centered', '\t<p style="text-align:center;">centered</p>'),

        ('p<>. justified', '\t<p style="text-align:justify;">justified</p>'),

        ('p(. left ident 1em', '\t<p style="padding-left:1em;">left ident 1em</p>'),

        ('p((. left ident 2em', '\t<p style="padding-left:2em;">left ident 2em</p>'),

        ('p))). right ident 3em', '\t<p style="padding-right:3em;">right ident 3em</p>'),

        ('h2()>. Bingo.', '\t<h2 style="padding-left:1em; padding-right:1em; text-align:right;">Bingo.</h2>'),

        ('h3()>[no]{color:red}. Bingo', '\t<h3 style="color:red; padding-left:1em; padding-right:1em; text-align:right;" lang="no">Bingo</h3>'),

        ('<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>',
         '<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>'),

        ('<div style="float:right;">\n\nh3. Sidebar\n\n"Hobix":http://hobix.com/\n"Ruby":http://ruby-lang.org/\n\n</div>\n\n'
         'The main text of the\npage goes here and will\nstay to the left of the\nsidebar.',
         '\t<p><div style="float:right;"></p>\n\n\t<h3>Sidebar</h3>\n\n\t<p><a href="http://hobix.com/">Hobix</a><br />\n'
         '<a href="http://ruby-lang.org/">Ruby</a></p>\n\n\t<p></div></p>\n\n\t<p>The main text of the<br />\n'
         'page goes here and will<br />\nstay to the left of the<br />\nsidebar.</p>'),

        ('# A first item\n# A second item\n# A third',
         '\t<ol>\n\t\t<li>A first item</li>\n\t\t<li>A second item</li>\n\t\t<li>A third</li>\n\t</ol>'),

        ('# Fuel could be:\n## Coal\n## Gasoline\n## Electricity\n# Humans need only:\n## Water\n## Protein',
         '\t<ol>\n\t\t<li>Fuel could be:\n\t<ol>\n\t\t<li>Coal</li>\n\t\t<li>Gasoline</li>\n\t\t<li>Electricity</li>\n\t</ol></li>\n\t\t'
         '<li>Humans need only:\n\t<ol>\n\t\t<li>Water</li>\n\t\t<li>Protein</li>\n\t</ol></li>\n\t</ol>'),

        ('* A first item\n* A second item\n* A third',
         '\t<ul>\n\t\t<li>A first item</li>\n\t\t<li>A second item</li>\n\t\t<li>A third</li>\n\t</ul>'),

        ('• A first item\n• A second item\n• A third',
         '\t<ul>\n\t\t<li>A first item</li>\n\t\t<li>A second item</li>\n\t\t<li>A third</li>\n\t</ul>'),

        ('* Fuel could be:\n** Coal\n** Gasoline\n** Electricity\n* Humans need only:\n** Water\n** Protein',
         '\t<ul>\n\t\t<li>Fuel could be:\n\t<ul>\n\t\t<li>Coal</li>\n\t\t<li>Gasoline</li>\n\t\t<li>Electricity</li>\n\t</ul></li>\n\t\t'
         '<li>Humans need only:\n\t<ul>\n\t\t<li>Water</li>\n\t\t<li>Protein</li>\n\t</ul></li>\n\t</ul>'),

        ('I searched "Google":http://google.com.', '\t<p>I searched <a href="http://google.com">Google</a>.</p>'),

        ('I searched "a search engine (Google)":http://google.com.', '\t<p>I searched <a href="http://google.com" title="Google">a search engine</a>.</p>'),

        ('I am crazy about "Hobix":hobix\nand "it\'s":hobix "all":hobix I ever\n"link to":hobix!\n\n[hobix]http://hobix.com',
         '\t<p>I am crazy about <a href="http://hobix.com">Hobix</a><br />\nand <a href="http://hobix.com">it’s</a> '
         '<a href="http://hobix.com">all</a> I ever<br />\n<a href="http://hobix.com">link to</a>!</p>'),

        ('!http://hobix.com/sample.jpg!', '\t<p><img alt="" src="http://hobix.com/sample.jpg" /></p>'),

        ('!openwindow1.gif(Bunny.)!', '\t<p><img alt="Bunny." src="openwindow1.gif" title="Bunny." /></p>'),

        ('!openwindow1.gif!:http://hobix.com/', '\t<p><a href="http://hobix.com/" class="img"><img alt="" src="openwindow1.gif" /></a></p>'),

        ('!>obake.gif!\n\nAnd others sat all round the small\nmachine and paid it to sing to them.',
         '\t<p><img align="right" alt="" src="obake.gif" /></p>\n\n\t'
         '<p>And others sat all round the small<br />\nmachine and paid it to sing to them.</p>'),

        ('We use CSS(Cascading Style Sheets).', '\t<p>We use <acronym title="Cascading Style Sheets"><span class="caps">CSS</span></acronym>.</p>'),

        ('|one|two|three|\n|a|b|c|',
         '\t<table>\n\t\t<tr>\n\t\t\t<td>one</td>\n\t\t\t<td>two</td>\n\t\t\t<td>three</td>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t</tr>\n\t</table>'),

        ('| name | age | sex |\n| joan | 24 | f |\n| archie | 29 | m |\n| bella | 45 | f |',
         '\t<table>\n\t\t<tr>\n\t\t\t<td> name </td>\n\t\t\t<td> age </td>\n\t\t\t<td> sex </td>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td> joan </td>\n\t\t\t<td> 24 </td>\n\t\t\t<td> f </td>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td> archie </td>\n\t\t\t<td> 29 </td>\n\t\t\t<td> m </td>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td> bella </td>\n\t\t\t<td> 45 </td>\n\t\t\t<td> f </td>\n\t\t</tr>\n\t</table>'),

        ('|_. name |_. age |_. sex |\n| joan | 24 | f |\n| archie | 29 | m |\n| bella | 45 | f |',
         '\t<table>\n\t\t<tr>\n\t\t\t<th>name </th>\n\t\t\t<th>age </th>\n\t\t\t<th>sex </th>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td> joan </td>\n\t\t\t<td> 24 </td>\n\t\t\t<td> f </td>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td> archie </td>\n\t\t\t<td> 29 </td>\n\t\t\t<td> m </td>\n\t\t</tr>'
         '\n\t\t<tr>\n\t\t\t<td> bella </td>\n\t\t\t<td> 45 </td>\n\t\t\t<td> f </td>\n\t\t</tr>\n\t</table>'),

        ('<script>alert("hello");</script>', '\t<p><script>alert(“hello”);</script></p>'),

        ('pre.. Hello\n\nHello Again\n\np. normal text', '<pre>Hello\n\nHello Again\n</pre>\n\n\t<p>normal text</p>'),

        ('<pre>this is in a pre tag</pre>', '<pre>this is in a pre tag</pre>'),

        ('"test1":http://foo.com/bar--baz\n\n"test2":http://foo.com/bar---baz\n\n"test3":http://foo.com/bar-17-18-baz',
         '\t<p><a href="http://foo.com/bar--baz">test1</a></p>\n\n\t'
         '<p><a href="http://foo.com/bar---baz">test2</a></p>\n\n\t'
         '<p><a href="http://foo.com/bar-17-18-baz">test3</a></p>'),

        ('"foo ==(bar)==":#foobar', '\t<p><a href="#foobar">foo (bar)</a></p>'),

        ('!http://render.mathim.com/A%5EtAx%20%3D%20A%5Et%28Ax%29.!',
         '\t<p><img alt="" src="http://render.mathim.com/A%5EtAx%20%3D%20A%5Et%28Ax%29." /></p>'),

        ('* Point one\n* Point two\n## Step 1\n## Step 2\n## Step 3\n* Point three\n** Sub point 1\n** Sub point 2',
         '\t<ul>\n\t\t<li>Point one</li>\n\t\t<li>Point two\n\t<ol>\n\t\t<li>Step 1</li>\n\t\t<li>Step 2</li>\n\t\t'
         '<li>Step 3</li>\n\t</ol></li>\n\t\t<li>Point three\n\t<ul>\n\t\t<li>Sub point 1</li>\n\t\t'
         '<li>Sub point 2</li>\n\t</ul></li>\n\t</ul>'),

        ('@array[4] = 8@', '\t<p><code>array[4] = 8</code></p>'),

        ('#{color:blue} one\n# two\n# three',
         '\t<ol style="color:blue;">\n\t\t<li>one</li>\n\t\t<li>two</li>\n\t\t<li>three</li>\n\t</ol>'),

        ('Links (like "this":http://foo.com), are now mangled in 2.1.0, whereas 2.0 parsed them correctly.',
         '\t<p>Links (like <a href="http://foo.com">this</a>), are now mangled in 2.1.0, whereas 2.0 parsed them correctly.</p>'),

        ('@monospaced text@, followed by text',
         '\t<p><code>monospaced text</code>, followed by text</p>'),

        ('h2. A header\n\n\n\n\n\nsome text', '\t<h2>A header</h2>\n\n\t<p>some text</p>'),

        ('*:(foo)foo bar baz*',
         '\t<p><strong cite="foo">foo bar baz</strong></p>'),

        ('pre.. foo bar baz\nquux', '<pre>foo bar baz\nquux\n</pre>'),

        ('line of text\n\n    leading spaces',
         '\t<p>line of text</p>\n\n    leading spaces'),

        ('"some text":http://www.example.com/?q=foo%20bar and more text',
         '\t<p><a href="http://www.example.com/?q=foo%20bar">some text</a> and more text</p>'),

        ('(??some text??)', '\t<p>(<cite>some text</cite>)</p>'),

        ('(*bold text*)', '\t<p>(<strong>bold text</strong>)</p>'),

        ('H[~2~]O', '\t<p>H<sub>2</sub>O</p>'),

        ("p=. Où est l'école, l'église s'il vous plaît?",
         """\t<p style="text-align:center;">Où est l’école, l’église s’il vous plaît?</p>"""),

        ("p=. *_The_* _*Prisoner*_",
         """\t<p style="text-align:center;"><strong><em>The</em></strong> <em><strong>Prisoner</strong></em></p>"""),

        ("""p=. "An emphasised _word._" & "*A spanned phrase.*" """,
         """\t<p style="text-align:center;">“An emphasised <em>word.</em>” & “<strong>A spanned phrase.</strong>” </p>"""),

        ("""p=. "*Here*'s a word!" """,
         """\t<p style="text-align:center;">“<strong>Here</strong>’s a word!” </p>"""),

        ("""p=. "Please visit our "Textile Test Page":http://textile.sitemonks.com" """,
         """\t<p style="text-align:center;">“Please visit our <a href="http://textile.sitemonks.com">Textile Test Page</a>” </p>"""),
        ("""| Foreign EXPÓŅÉNTIAL |""",
         """\t<table>\n\t\t<tr>\n\t\t\t<td> Foreign <span class="caps">EXPÓŅÉNTIAL</span> </td>\n\t\t</tr>\n\t</table>"""),
        ("""Piękne ŹDŹBŁO""",
         """\t<p>Piękne <span class="caps">ŹDŹBŁO</span></p>"""),

        ("""p=. Tell me, what is AJAX(Asynchronous Javascript and XML), please?""",
         """\t<p style="text-align:center;">Tell me, what is <acronym title="Asynchronous Javascript and XML"><span class="caps">AJAX</span></acronym>, please?</p>"""),
        ('p{font-size:0.8em}. *TxStyle* is a documentation project of Textile 2.4 for "Textpattern CMS":http://texpattern.com.',
         '\t<p style="font-size:0.8em;"><strong>TxStyle</strong> is a documentation project of Textile 2.4 for <a href="http://texpattern.com">Textpattern <span class="caps">CMS</span></a>.</p>'),
        (""""Übermensch":http://de/wikipedia.org/wiki/Übermensch""", """\t<p><a href="http://de/wikipedia.org/wiki/%C3%9Cbermensch">Übermensch</a></p>"""),
        ("""Here is some text with a <!-- Commented out[1] --> block.\n\n<!-- Here is a single <span>line</span> comment block -->\n\n<!-- Here is a whole\nmultiline\n<span>HTML</span>\nComment\n-->\n\nbc. <!-- Here is a comment block in a code block. -->""",
         """\t<p>Here is some text with a<!-- Commented out[1] --> block.</p>\n\n\t<p><!-- Here is a single <span>line</span> comment block --></p>\n\n\t<p><!-- Here is a whole\nmultiline\n<span>HTML</span>\nComment\n--></p>\n\n<pre><code><!-- Here is a comment block in a code block. -->\n</code></pre>"""),
        (""""Textile(c)" is a registered(r) 'trademark' of Textpattern(tm) -- or TXP(That's textpattern!) -- at least it was - back in '88 when 2x4 was (+/-)5(o)C ... QED!\n\np{font-size: 200%;}. 2(1/4) 3(1/2) 4(3/4)""",
         """\t<p>“Textile©” is a registered® ‘trademark’ of Textpattern™ — or <acronym title="That’s textpattern!"><span class="caps">TXP</span></acronym> — at least it was – back in ’88 when 2×4 was ±5°C … <span class="caps">QED</span>!</p>\n\n\t<p style="font-size: 200%;">2¼ 3½ 4¾</p>"""),
        ("""|=. Testing colgroup and col syntax\n|:\\5. 80\n|a|b|c|d|e|\n\n|=. Testing colgroup and col syntax|\n|:\\5. 80|\n|a|b|c|d|e|""", """\t<table>\n\t<caption>Testing colgroup and col syntax</caption>\n\t<colgroup span="5" width="80">\n\t</colgroup>\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n\t</table>\n\n\t<table>\n\t<caption>Testing colgroup and col syntax</caption>\n\t<colgroup span="5" width="80">\n\t</colgroup>\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n\t</table>"""),
        ("""table(#dvds){border-collapse:collapse}. Great films on DVD employing Textile summary, caption, thead, tfoot, two tbody elements and colgroups\n|={font-size:140%;margin-bottom:15px}. DVDs with two Textiled tbody elements\n|:\\3. 100 |{background:#ddd}|250||50|300|\n|^(header).\n|_. Title |_. Starring |_. Director |_. Writer |_. Notes |\n|~(footer).\n|\\5=. This is the tfoot, centred |\n|-(toplist){background:#c5f7f6}.\n| _The Usual Suspects_ | Benicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey | Bryan Singer | Chris McQaurrie | One of the finest films ever made |\n| _Se7en_ | Morgan Freeman, Brad Pitt, Kevin Spacey | David Fincher | Andrew Kevin Walker | Great psychological thriller |\n| _Primer_ | David Sullivan, Shane Carruth | Shane Carruth | Shane Carruth | Amazing insight into trust and human psychology <br />rather than science fiction. Terrific! |\n| _District 9_ | Sharlto Copley, Jason Cope | Neill Blomkamp | Neill Blomkamp, Terri Tatchell | Social commentary layered on thick,\nbut boy is it done well |\n|-(medlist){background:#e7e895;}.\n| _Arlington Road_ | Tim Robbins, Jeff Bridges | Mark Pellington | Ehren Kruger | Awesome study in neighbourly relations |\n| _Phone Booth_ | Colin Farrell, Kiefer Sutherland, Forest Whitaker | Joel Schumacher | Larry Cohen | Edge-of-the-seat stuff in this\nshort but brilliantly executed thriller |""",
         """\t<table style="border-collapse:collapse;" id="dvds" summary="Great films on DVD employing Textile summary, caption, thead, tfoot, two tbody elements and colgroups">\n\t<caption style="font-size:140%; margin-bottom:15px;"><span class="caps">DVD</span>s with two Textiled tbody elements</caption>\n\t<colgroup span="3" width="100">\n\t<col style="background:#ddd;" />\n\t<col width="250" />\n\t<col />\n\t<col width="50" />\n\t<col width="300" />\n\t</colgroup>\n\t<thead class="header">\n\t\t<tr>\n\t\t\t<th>Title </th>\n\t\t\t<th>Starring </th>\n\t\t\t<th>Director </th>\n\t\t\t<th>Writer </th>\n\t\t\t<th>Notes </th>\n\t\t</tr>\n\t</thead>\n\t<tfoot class="footer">\n\t\t<tr>\n\t\t\t<td style="text-align:center;" colspan="5">This is the tfoot, centred </td>\n\t\t</tr>\n\t</tfoot>\n\t<tbody style="background:#c5f7f6;" class="toplist">\n\t\t<tr>\n\t\t\t<td> <em>The Usual Suspects</em> </td>\n\t\t\t<td> Benicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey </td>\n\t\t\t<td> Bryan Singer </td>\n\t\t\t<td> Chris McQaurrie </td>\n\t\t\t<td> One of the finest films ever made </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Se7en</em> </td>\n\t\t\t<td> Morgan Freeman, Brad Pitt, Kevin Spacey </td>\n\t\t\t<td> David Fincher </td>\n\t\t\t<td> Andrew Kevin Walker </td>\n\t\t\t<td> Great psychological thriller </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Primer</em> </td>\n\t\t\t<td> David Sullivan, Shane Carruth </td>\n\t\t\t<td> Shane Carruth </td>\n\t\t\t<td> Shane Carruth </td>\n\t\t\t<td> Amazing insight into trust and human psychology <br />\nrather than science fiction. Terrific! </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>District 9</em> </td>\n\t\t\t<td> Sharlto Copley, Jason Cope </td>\n\t\t\t<td> Neill Blomkamp </td>\n\t\t\t<td> Neill Blomkamp, Terri Tatchell </td>\n\t\t\t<td> Social commentary layered on thick,<br />\nbut boy is it done well </td>\n\t\t</tr>\n\t</tbody>\n\t<tbody style="background:#e7e895;" class="medlist">\n\t\t<tr>\n\t\t\t<td> <em>Arlington Road</em> </td>\n\t\t\t<td> Tim Robbins, Jeff Bridges </td>\n\t\t\t<td> Mark Pellington </td>\n\t\t\t<td> Ehren Kruger </td>\n\t\t\t<td> Awesome study in neighbourly relations </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Phone Booth</em> </td>\n\t\t\t<td> Colin Farrell, Kiefer Sutherland, Forest Whitaker </td>\n\t\t\t<td> Joel Schumacher </td>\n\t\t\t<td> Larry Cohen </td>\n\t\t\t<td> Edge-of-the-seat stuff in this<br />\nshort but brilliantly executed thriller </td>\n\t\t</tr>\n\t</tbody>\n\t</table>"""),
        ("""-(hot) *coffee* := Hot _and_ black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk := Nourishing beverage for baby cows.\nCold drink that goes great with cookies. =:\n\n-(hot) coffee := Hot and black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk :=\nNourishing beverage for baby cows.\nCold drink that goes great with cookies. =:""",
        """<dl>\n\t<dt class="hot"><strong>coffee</strong></dt>\n\t<dd>Hot <em>and</em> black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</dd>\n</dl>\n\n<dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd>\n</dl>"""),
        (""";(class#id) Term 1\n: Def 1\n: Def 2\n: Def 3""",
         """\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3</dd>\n\t</dl>"""),
        ("""*Here is a comment*\n\nHere is *(class)a comment*\n\n*(class)Here is a class* that is a little extended and is\n*followed* by a strong word!\n\nbc. ; Content-type: text/javascript\n; Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\n; Expires: Sat, 24 Jul 2003 05:00:00 GMT\n; Last-Modified: Wed, 1 Jan 2025 05:00:00 GMT\n; Pragma: no-cache\n\n*123 test*\n\n*test 123*\n\n**123 test**\n\n**test 123**""",
         """\t<p><strong>Here is a comment</strong></p>\n\n\t<p>Here is <strong class="class">a comment</strong></p>\n\n\t<p><strong class="class">Here is a class</strong> that is a little extended and is<br />\n<strong>followed</strong> by a strong word!</p>\n\n<pre><code>; Content-type: text/javascript\n; Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\n; Expires: Sat, 24 Jul 2003 05:00:00 GMT\n; Last-Modified: Wed, 1 Jan 2025 05:00:00 GMT\n; Pragma: no-cache\n</code></pre>\n\n\t<p><strong>123 test</strong></p>\n\n\t<p><strong>test 123</strong></p>\n\n\t<p><b>123 test</b></p>\n\n\t<p><b>test 123</b></p>"""),
        ("""#_(first#list) one\n# two\n# three\n\ntest\n\n#(ordered#list2).\n# one\n# two\n# three\n\ntest\n\n#_(class_4).\n# four\n# five\n# six\n\ntest\n\n#_ seven\n# eight\n# nine\n\ntest\n\n# one\n# two\n# three\n\ntest\n\n#22 22\n# 23\n# 24""",
         """\t<ol class="first" id="list" start="1">\n\t\t<li>one</li>\n\t\t<li>two</li>\n\t\t<li>three</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol class="ordered" id="list2">\n\t\t<li>one</li>\n\t\t<li>two</li>\n\t\t<li>three</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol class="class_4" start="4">\n\t\t<li>four</li>\n\t\t<li>five</li>\n\t\t<li>six</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol start="7">\n\t\t<li>seven</li>\n\t\t<li>eight</li>\n\t\t<li>nine</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol>\n\t\t<li>one</li>\n\t\t<li>two</li>\n\t\t<li>three</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol start="22">\n\t\t<li>22</li>\n\t\t<li>23</li>\n\t\t<li>24</li>\n\t</ol>"""),
        ("""# one\n##3 one.three\n## one.four\n## one.five\n# two\n\ntest\n\n#_(continuation#section2).\n# three\n# four\n##_ four.six\n## four.seven\n# five\n\ntest\n\n#21 twenty-one\n# twenty-two""",
         """\t<ol>\n\t\t<li>one\n\t<ol start="3">\n\t\t<li>one.three</li>\n\t\t<li>one.four</li>\n\t\t<li>one.five</li>\n\t</ol></li>\n\t\t<li>two</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol class="continuation" id="section2" start="3">\n\t\t<li>three</li>\n\t\t<li>four\n\t<ol start="6">\n\t\t<li>four.six</li>\n\t\t<li>four.seven</li>\n\t</ol></li>\n\t\t<li>five</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol start="21">\n\t\t<li>twenty-one</li>\n\t\t<li>twenty-two</li>\n\t</ol>"""),
        ("""|* Foo[^2^]\n* _bar_\n* ~baz~ |\n|#4 *Four*\n# __Five__ |\n|-(hot) coffee := Hot and black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk :=\nNourishing beverage for baby cows.\nCold drink that goes great with cookies. =:\n|""",
         """\t<table>\n\t\t<tr>\n\t\t\t<td>\t<ul>\n\t\t<li>Foo<sup>2</sup></li>\n\t\t<li><em>bar</em></li>\n\t\t<li><sub>baz</sub></li>\n\t</ul></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\t<ol start="4">\n\t\t<li><strong>Four</strong></li>\n\t\t<li><i>Five</i></li>\n\t</ol></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td><dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd><br />\n</dl></td>\n\t\t</tr>\n\t</table>"""),
        ("""h4. A more complicated table\n\ntable(tableclass#tableid){color:blue}.\n|_. table |_. more |_. badass |\n|\\3. Horizontal span of 3|\n(firstrow). |first|HAL(open the pod bay doors)|1|\n|some|{color:green}. styled|content|\n|/2. spans 2 rows|this is|quite a|\n| deep test | don't you think?|\n(lastrow). |fifth|I'm a lumberjack|5|\n|sixth| _*bold italics*_ |6|""",
         """\t<h4>A more complicated table</h4>\n\n\t<table style="color:blue;" class="tableclass" id="tableid">\n\t\t<tr>\n\t\t\t<th>table </th>\n\t\t\t<th>more </th>\n\t\t\t<th>badass </th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td colspan="3">Horizontal span of 3</td>\n\t\t</tr>\n\t\t<tr class="firstrow">\n\t\t\t<td>first</td>\n\t\t\t<td><acronym title="open the pod bay doors"><span class="caps">HAL</span></acronym></td>\n\t\t\t<td>1</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>some</td>\n\t\t\t<td style="color:green;">styled</td>\n\t\t\t<td>content</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td rowspan="2">spans 2 rows</td>\n\t\t\t<td>this is</td>\n\t\t\t<td>quite a</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> deep test </td>\n\t\t\t<td> don’t you think?</td>\n\t\t</tr>\n\t\t<tr class="lastrow">\n\t\t\t<td>fifth</td>\n\t\t\t<td>I’m a lumberjack</td>\n\t\t\t<td>5</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>sixth</td>\n\t\t\t<td> <em><strong>bold italics</strong></em> </td>\n\t\t\t<td>6</td>\n\t\t</tr>\n\t</table>"""),
        ("""| *strong* |\n\n| _em_ |\n\n| Inter-word -dashes- | ZIP-codes are 5- or 9-digit codes |""",
         """\t<table>\n\t\t<tr>\n\t\t\t<td> <strong>strong</strong> </td>\n\t\t</tr>\n\t</table>\n\n\t<table>\n\t\t<tr>\n\t\t\t<td> <em>em</em> </td>\n\t\t</tr>\n\t</table>\n\n\t<table>\n\t\t<tr>\n\t\t\t<td> Inter-word <del>dashes</del> </td>\n\t\t\t<td> <span class="caps">ZIP</span>-codes are 5- or 9-digit codes </td>\n\t\t</tr>\n\t</table>"""),
        ("""|_. attribute list |\n|<. align left |\n|>. align right|\n|=. center |\n|<>. justify me|\n|^. valign top |\n|~. bottom |""",
         """\t<table>\n\t\t<tr>\n\t\t\t<th>attribute list </th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style="text-align:left;">align left </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style="text-align:right;">align right</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style="text-align:center;">center </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style="text-align:justify;">justify me</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style="vertical-align:top;">valign top </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td style="vertical-align:bottom;">bottom </td>\n\t\t</tr>\n\t</table>"""),
        ("""h2. A definition list\n\n;(class#id) Term 1\n: Def 1\n: Def 2\n: Def 3\n;; Center\n;; NATO(Why Em Cee Ayy)\n:: Subdef 1\n:: Subdef 2\n;;; SubSub Term\n::: SubSub Def 1\n::: SubSub Def 2\n::: Subsub Def 3\nWith newline\n::: Subsub Def 4\n:: Subdef 3\n: DEF 4\n; Term 2\n: Another def\n: And another\n: One more\n:: A def without a term\n:: More defness\n; Third term for good measure\n: My definition of a boombastic jazz""",
         """\t<h2>A definition list</h2>\n\n\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3\n\t<dl>\n\t\t<dt>Center</dt>\n\t\t<dt><acronym title="Why Em Cee Ayy"><span class="caps">NATO</span></acronym></dt>\n\t\t<dd>Subdef 1</dd>\n\t\t<dd>Subdef 2\n\t<dl>\n\t\t<dt>SubSub Term</dt>\n\t\t<dd>SubSub Def 1</dd>\n\t\t<dd>SubSub Def 2</dd>\n\t\t<dd>Subsub Def 3<br />\nWith newline</dd>\n\t\t<dd>Subsub Def 4</dd>\n\t</dl></dd>\n\t\t<dd>Subdef 3</dd>\n\t</dl></dd>\n\t\t<dd><span class="caps">DEF</span> 4</dd>\n\t\t<dt>Term 2</dt>\n\t\t<dd>Another def</dd>\n\t\t<dd>And another</dd>\n\t\t<dd>One more\n\t<dl>\n\t\t<dd>A def without a term</dd>\n\t\t<dd>More defness</dd>\n\t</dl></dd>\n\t\t<dt>Third term for good measure</dt>\n\t\t<dd>My definition of a boombastic jazz</dd>\n\t</dl>"""),
        ("""###. Here's a comment.\n\nh3. Hello\n\n###. And\nanother\none.\n\nGoodbye.""", """\t<h3>Hello</h3>\n\n\t<p>Goodbye.</p>"""),
        ("""h2. A Definition list which covers the instance where a new definition list is created with a term without a definition\n\n- term :=\n- term2 := def""", """\t<h2>A Definition list which covers the instance where a new definition list is created with a term without a definition</h2>\n\n<dl>\n\t<dt>term2</dt>\n\t<dd>def</dd>\n</dl>"""),
        ('!{height:20px;width:20px;}https://1.gravatar.com/avatar/!',
         '\t<p><img alt="" src="https://1.gravatar.com/avatar/" style="height:20px; width:20px;" /></p>')

    )

    # A few extra cases for HTML4
    html_known_values = (
        ('I spoke.\nAnd none replied.', '\t<p>I spoke.<br />\nAnd none replied.</p>'),
        ('I __know__.\nI **really** __know__.', '\t<p>I <i>know</i>.<br />\nI <b>really</b> <i>know</i>.</p>'),
        ("I'm %{color:red}unaware%\nof most soft drinks.", '\t<p>I’m <span style="color:red;">unaware</span><br />\nof most soft drinks.</p>'),
        ('I seriously *{color:red}blushed*\nwhen I _(big)sprouted_ that\ncorn stalk from my\n%[es]cabeza%.',
        '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>'
        ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'),
        ('<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>',
         '<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>'),
        ('<div style="float:right;">\n\nh3. Sidebar\n\n"Hobix":http://hobix.com/\n"Ruby":http://ruby-lang.org/\n\n</div>\n\n'
         'The main text of the\npage goes here and will\nstay to the left of the\nsidebar.',
         '\t<p><div style="float:right;"></p>\n\n\t<h3>Sidebar</h3>\n\n\t<p><a href="http://hobix.com/">Hobix</a><br />\n'
         '<a href="http://ruby-lang.org/">Ruby</a></p>\n\n\t<p></div></p>\n\n\t<p>The main text of the<br />\n'
         'page goes here and will<br />\nstay to the left of the<br />\nsidebar.</p>'),
        ('I am crazy about "Hobix":hobix\nand "it\'s":hobix "all":hobix I ever\n"link to":hobix!\n\n[hobix]http://hobix.com',
         '\t<p>I am crazy about <a href="http://hobix.com">Hobix</a><br />\nand <a href="http://hobix.com">it’s</a> '
         '<a href="http://hobix.com">all</a> I ever<br />\n<a href="http://hobix.com">link to</a>!</p>'),
        ('!http://hobix.com/sample.jpg!', '\t<p><img alt="" src="http://hobix.com/sample.jpg" /></p>'),
        ('!openwindow1.gif(Bunny.)!', '\t<p><img alt="Bunny." src="openwindow1.gif" title="Bunny." /></p>'),
        ('!openwindow1.gif!:http://hobix.com/', '\t<p><a href="http://hobix.com/" class="img"><img alt="" src="openwindow1.gif" /></a></p>'),
        ('!>obake.gif!\n\nAnd others sat all round the small\nmachine and paid it to sing to them.',
         '\t<p><img align="right" alt="" src="obake.gif" /></p>\n\n\t'
         '<p>And others sat all round the small<br />\nmachine and paid it to sing to them.</p>'),
        ('!http://render.mathim.com/A%5EtAx%20%3D%20A%5Et%28Ax%29.!',
         '\t<p><img alt="" src="http://render.mathim.com/A%5EtAx%20%3D%20A%5Et%28Ax%29." /></p>'),
        ('notextile. <b> foo bar baz</b>\n\np. quux\n',
         '<b> foo bar baz</b>\n\n\t<p>quux</p>')
    )

    def testKnownValuesXHTML(self):
        # XHTML
        for t, h in self.xhtml_known_values:
            yield self.check_textile, t, h, 'xhtml'

    def testKnownValuesHTML(self):
        # HTML5
        for t, h in self.html_known_values:
            yield self.check_textile, t, h, 'html5'

    def check_textile(self, input, expected_output, html_type):
        output = textile.textile(input, html_type=html_type)
        eq_(output, expected_output)


class Tests():
    def testFootnoteReference(self):
        html = textile.textile('YACC[1]')
        assert_true(re.search(r'^\t<p><span class="caps">YACC</span><sup class="footnote" id="fnrev[a-f0-9]{32}"><a href="#fn[a-f0-9]{32}">1</a></sup></p>', html))

    def testFootnote(self):
        html = textile.textile('This is covered elsewhere[1].\n\nfn1. Down here, in fact.\n\nfn2. Here is another footnote.')
        assert_true(re.search(r'^\t<p>This is covered elsewhere<sup class="footnote" id="fnrev[a-f0-9]{32}"><a href="#fn([a-f0-9]{32})">1</a></sup>.</p>\n\n\t<p class="footnote" id="fn\1"><sup>1</sup> Down here, in fact.</p>\n\n\t<p class="footnote" id="fn2"><sup>2</sup> Here is another footnote.</p>$', html))

        html = textile.textile('''See[1] for details -- or perhaps[100] at a push.\n\nfn1. Here are the details.\n\nfn100(footy#otherid). A totally unrelated footnote.''')
        assert_true(re.search(r'^\t<p>See<sup class="footnote" id="fnrev[a-f0-9]{32}"><a href="#fn([a-f0-9]{32})">1</a></sup> for details — or perhaps<sup class="footnote" id="fnrev[a-f0-9]{32}"><a href="#fn([a-f0-9]{32})">100</a></sup> at a push.</p>\n\n\t<p class="footnote" id="fn\1"><sup>1</sup> Here are the details.</p>\n\n\t<p class="footy" id="otherid"><sup id="fn\2">100</sup> A totally unrelated footnote.</p>$', html))

        html = textile.textile('''See[2] for details, and later, reference it again[2].\n\nfn2^(footy#otherid)[en]. Here are the details.''')
        assert_true(re.search(r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})"><a href="#fn([a-f0-9]{32})">2</a></sup> for details, and later, reference it again<sup class="footnote"><a href="#fn\2">2</a></sup>.</p>\n\n\t<p class="footy" id="otherid" lang="en"><sup id="fn\2"><a href="#fnrev\1">2</a></sup> Here are the details.</p>$', html))

        html = textile.textile('''See[3!] for details.\n\nfn3. Here are the details.''')
        assert_true(re.search(r'^\t<p>See<sup class="footnote" id="fnrev[a-f0-9]{32}">3</sup> for details.</p>\n\n\t<p class="footnote" id="fn[a-f0-9]{32}"><sup>3</sup> Here are the details.</p>$', html))

        html = textile.textile('''See[4!] for details.\n\nfn4^. Here are the details.''')
        assert_true(re.search(r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})">4</sup> for details.</p>\n\n\t<p class="footnote" id="fn[a-f0-9]{32}"><sup><a href="#fnrev\1">4</a></sup> Here are the details.</p>$', html))

    def testURLWithHyphens(self):
        eq_(textile.textile('"foo":http://google.com/one--two'), '\t<p><a href="http://google.com/one--two">foo</a></p>')

    def testIssue024TableColspan(self):
        eq_(textile.textile('|\\2. spans two cols |\n| col 1 | col 2 |'),
            '\t<table>\n\t\t<tr>\n\t\t\t<td colspan="2">spans two cols </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> col 1 </td>\n\t\t\t<td> col 2 </td>\n\t\t</tr>\n\t</table>')

    def testPBAColspan(self):
        eq_(textile.Textile().pba(r'\3', element='td'), ' colspan="3"')

    def testIssue002Escaping(self):
        foo = '"foo ==(bar)==":#foobar'
        eq_(textile.textile(foo), '\t<p><a href="#foobar">foo (bar)</a></p>')

    def testIssue014NewlinesInExtendedPreBlocks(self):
        text = "pre.. Hello\n\nAgain\n\np. normal text"
        eq_(textile.textile(text), '<pre>Hello\n\nAgain\n</pre>\n\n\t<p>normal text</p>')

    def testURLWithParens(self):
        text = '"python":http://en.wikipedia.org/wiki/Python_(programming_language)'
        expect = '\t<p><a href="http://en.wikipedia.org/wiki/Python_%28programming_language%29">python</a></p>'
        result = textile.textile(text)
        eq_(result, expect)

    def testTableWithHyphenStyles(self):
        text = 'table(linkblog-thumbnail).\n|(linkblog-thumbnail-cell). apple|bear|'
        expect = '\t<table class="linkblog-thumbnail">\n\t\t<tr>\n\t\t\t<td style="vertical-align:middle;" class="linkblog-thumbnail-cell">apple</td>\n\t\t\t<td>bear</td>\n\t\t</tr>\n\t</table>'
        result = textile.textile(text)
        eq_(result, expect)

    def testHeadOffset(self):
        text = 'h2. This is a header'
        head_offset = 2
        expect = '\t<h4>This is a header</h4>'
        result = textile.textile(text, head_offset=head_offset)
        eq_(result, expect)

    def testIssue035(self):
        result = textile.textile('"z"')
        expect = '\t<p>“z” </p>'
        eq_(result, expect)

        result = textile.textile('" z"')
        expect = '\t<p>“ z” </p>'
        eq_(result, expect)

    def testIssue032(self):
        text = "|thing|||otherthing|"
        result = textile.textile(text)
        expect = "\t<table>\n\t\t<tr>\n\t\t\t<td>thing</td>\n\t\t\t<td></td>\n\t\t\t<td></td>\n\t\t\t<td>otherthing</td>\n\t\t</tr>\n\t</table>"
        eq_(result, expect)

    def testIssue036(self):
        test = '"signup":signup\n[signup]http://myservice.com/signup'
        result = textile.textile(test)
        expect = '\t<p><a href="http://myservice.com/signup">signup</a></p>'
        eq_(result, expect)

        test = '"signup":signup\n[signup]https://myservice.com/signup'
        result = textile.textile(test)
        expect = '\t<p><a href="https://myservice.com/signup">signup</a></p>'
        eq_(result, expect)

    def testNestedFormatting(self):
        test = "*_test text_*"
        result = textile.textile(test)
        expect = "\t<p><strong><em>test text</em></strong></p>"

        eq_(result, expect)

        test = "_*test text*_"
        result = textile.textile(test)
        expect = "\t<p><em><strong>test text</strong></em></p>"

        eq_(result, expect)

    def testRestricted(self):
        test = "this is \"some\" *bold text*."
        result = textile.textile_restricted(test)
        expect = "\t<p>this is “some” <strong>bold text</strong>.</p>"

        eq_(result, expect)

        #Note that the HTML is escaped, thus rendering
        #the <script> tag harmless.
        test = "Here is some text.\n<script>alert('hello world')</script>"
        result = textile.textile_restricted(test)
        expect = "\t<p>Here is some text.<br />\n<script>alert(‘hello world’)</script></p>"

        eq_(result, expect)

        test = "Here's some <!-- commented *out* --> text."
        result = textile.textile_restricted(test)
        expect = "\t<p>Here’s some <!— commented <strong>out</strong> —> text.</p>"

        eq_(result, expect)

    def testQuotesInCode(self):
        test = "<code>'quoted string'</code>"
        result = textile.textile(test)
        expect = "\t<p><code>'quoted string'</code></p>"

        eq_(result, expect)

    def testUnicodeFootnote(self):
        html = textile.textile('текст[1]')
        assert_true(re.compile('^\t<p>текст<sup class="footnote" id="fnrev[a-f0-9]{32}"><a href="#fn[a-f0-9]{32}">1</a></sup></p>', re.U).search(html))

    def testAutoLinking(self):
        test = """some text "test":http://www.google.com http://www.google.com "$":http://www.google.com"""
        result = """\t<p>some text <a href="http://www.google.com">test</a> <a href="http://www.google.com">www.google.com</a> <a href="http://www.google.com">www.google.com</a></p>"""
        expect = textile.textile(test, auto_link=True)

        eq_(result, expect)

    def testPre(self):
        test = "<pre>some preformatted text</pre>other text"
        result = "\t<p><pre>some preformatted text</pre>other text</p>"
        expect = textile.textile(test)

        eq_(result, expect)

    def testSanitize(self):
        try:
            __import__('html5lib')
        except ImportError:
            raise SkipTest()

        test = "a paragraph of benign text"
        result = "\t<p>a paragraph of benign text</p>"
        expect = textile.Textile().parse(test, sanitize=True)
        eq_(result, expect)

        test = """<p style="width: expression(alert('evil'));">a paragraph of evil text</p>"""
        result = '<p style="">a paragraph of evil text</p>'
        expect = textile.Textile().parse(test, sanitize=True)
        eq_(result, expect)

        test = """<p>a paragraph of benign text<br />and more text</p>"""
        result = '<p>a paragraph of benign text<br />\nand more text</p>'
        expect = textile.Textile(html_type='html5').parse(test, sanitize=True)
        eq_(result, expect)

    def testImageSize(self):
        try:
            __import__('PIL')
        except ImportError:
            raise SkipTest()

        test = "!http://www.google.com/intl/en_ALL/images/srpr/logo1w.png!"
        result = '\t<p><img alt="" height="95" src="http://www.google.com/intl/en_ALL/images/srpr/logo1w.png" width="275" /></p>'
        expect = textile.Textile(get_sizes=True).parse(test)
        eq_(result, expect)

    def testAtSignAndNotextileInTable(self):
        test = "|@<A1>@|@<A2>@ @<A3>@|\n|<notextile>*B1*</notextile>|<notextile>*B2*</notextile> <notextile>*B3*</notextile>|"
        result = "\t<table>\n\t\t<tr>\n\t\t\t<td><code><A1></code></td>\n\t\t\t<td><code><A2></code> <code><A3></code></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>*B1*</td>\n\t\t\t<td>*B2* *B3*</td>\n\t\t</tr>\n\t</table>"
        expect = textile.textile(test)
        eq_(result, expect)

    def testEndnotesSimple(self):
        test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!.\n\nnote#my_first_label Over the past billion years, about a quarter of the moon's 4.5 billion-year lifespan, it has shrunk about 200 meters (700 feet) in diameter."""
        html = textile.textile(test)
        result_pattern = r"""\t<p>Scientists say the moon is slowly shrinking<sup><a href="#(note[a-f0-9]{32})"><span id="noteref[a-f0-9]{32}">1</span></a></sup>.</p>\n\n\t<ol>\n\t<li><span id="\1"> </span>Over the past billion years, about a quarter of the moon’s 4.5 billion-year lifespan, it has shrunk about 200 meters \(700 feet\) in diameter.</li>\n</ol>$"""
        result_re = re.compile(result_pattern)
        assert_true(result_re.search(html))

    def testEndnotesComplex(self):
        test = """Tim Berners-Lee is one of the pioneer voices in favour of Net Neutrality[#netneutral] and has expressed the view that ISPs should supply "connectivity with no strings attached"[#netneutral!] [#tbl_quote]\n\nBerners-Lee admitted that the forward slashes ("//") in a web address were actually unnecessary.  He told the newspaper that he could easily have designed URLs not to have the forward slashes.  "... it seemed like a good idea at the time,"[#slashes]\n\nnote#netneutral. "Web creator rejects net tracking":http://news.bbc.co.uk/2/hi/technology/7613201.stm. BBC. 15 September 2008\n\nnote#tbl_quote. "Web inventor's warning on spy software":http://www.telegraph.co.uk/news/uknews/1581938/Web-inventor%27s-warning-on-spy-software.html. The Daily Telegraph (London). 25 May 2008\n\nnote#slashes. "Berners-Lee 'sorry' for slashes":http://news.bbc.co.uk/1/hi/technology/8306631.stm. BBC. 14 October 2009\n\nnotelist."""
        html = textile.textile(test)
        result_pattern = r"""\t<p>Tim Berners-Lee is one of the pioneer voices in favour of Net Neutrality<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">1</span></a></sup> and has expressed the view that <span class="caps">ISP</span>s should supply “connectivity with no strings attached”<sup><span id="(noteref[a-f0-9]{32})">1</span></sup> <sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">2</span></a></sup></p>\n\n\t<p>Berners-Lee admitted that the forward slashes \(”//”\) in a web address were actually unnecessary.  He told the newspaper that he could easily have designed <span class="caps">URL</span>s not to have the forward slashes.  “… it seemed like a good idea at the time,”<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">3</span></a></sup></p>\n\n\t<ol>\n\t<li><sup><a href="#\2">a</a></sup> <sup><a href="#\3">b</a></sup><span id="\1"> </span><a href="http://news.bbc.co.uk/2/hi/technology/7613201.stm">Web creator rejects net tracking</a>. <span class="caps">BBC</span>. 15 September 2008</li>\n\t<li><sup><a href="#\5">a</a></sup><span id="\4"> </span><a href="http://www.telegraph.co.uk/news/uknews/1581938/Web-inventor%27s-warning-on-spy-software.html">Web inventor’s warning on spy software</a>. The Daily Telegraph \(London\). 25 May 2008</li>\n\t<li><sup><a href="#\7">a</a></sup><span id="\6"> </span><a href="http://news.bbc.co.uk/1/hi/technology/8306631.stm">Berners-Lee ‘sorry’ for slashes</a>. <span class="caps">BBC</span>. 14 October 2009</li>\n</ol>$"""
        result_re = re.compile(result_pattern)
        assert_true(result_re.search(html))

    def testEndnotesUnreferencedNote(self):
        test = """Scientists say[#lavader] the moon is quite small. But I, for one, don't believe them. Others claim it to be made of cheese[#aardman]. If this proves true I suspect we are in for troubled times[#apollo13] as people argue over their "share" of the moon's cheese. In the end, its limited size[#lavader] may prove problematic.\n\nnote#lavader(noteclass). "Proof of the small moon hypothesis":http://antwrp.gsfc.nasa.gov/apod/ap080801.html. Copyright(c) Laurent Laveder\n\nnote#aardman(#noteid). "Proof of a cheese moon":http://www.imdb.com/title/tt0104361\n\nnote#apollo13. After all, things do go "wrong":http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:§^.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:‡"""
        html = textile.textile(test)
        result_pattern = r"""\t<p>Scientists say<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">1</span></a></sup> the moon is quite small. But I, for one, don’t believe them. Others claim it to be made of cheese<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">2</span></a></sup>. If this proves true I suspect we are in for troubled times<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">3</span></a></sup> as people argue over their “share” of the moon’s cheese. In the end, its limited size<sup><a href="#\1"><span id="(noteref[a-f0-9]{32})">1</span></a></sup> may prove problematic.</p>\n\n\t<ol style="padding:1em; margin:1em; border-bottom:1px solid gray;">\n\t<li class="noteclass"><sup><a href="#\2">a</a></sup> <sup><a href="#\7">b</a></sup><span id="\1"> </span><a href="http://antwrp.gsfc.nasa.gov/apod/ap080801.html">Proof of the small moon hypothesis</a>. Copyright© Laurent Laveder</li>\n\t<li id="noteid"><sup><a href="#\4">a</a></sup><span id="\3"> </span><a href="http://www.imdb.com/title/tt0104361">Proof of a cheese moon</a></li>\n\t<li><sup><a href="#\6">a</a></sup><span id="\5"> </span>After all, things do go <a href="http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident">wrong</a>.</li>\n</ol>\n\n\t<ol style="padding:1em; margin:1em; border-bottom:1px solid gray;">\n\t<li class="noteclass"><sup><a href="#\2">\xa7</a></sup><span id="\1"> </span><a href="http://antwrp.gsfc.nasa.gov/apod/ap080801.html">Proof of the small moon hypothesis</a>. Copyright© Laurent Laveder</li>\n\t<li id="noteid"><sup><a href="#\4">\xa7</a></sup><span id="\3"> </span><a href="http://www.imdb.com/title/tt0104361">Proof of a cheese moon</a></li>\n\t<li><sup><a href="#\6">\xa7</a></sup><span id="\5"> </span>After all, things do go <a href="http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident">wrong</a>.</li>\n</ol>\n\n\t<ol style="padding:1em; margin:1em; border-bottom:1px solid gray;">\n\t<li class="noteclass"><sup><a href="#\2">‡</a></sup> <sup><a href="#\7">‡</a></sup><span id="\1"> </span><a href="http://antwrp.gsfc.nasa.gov/apod/ap080801.html">Proof of the small moon hypothesis</a>. Copyright© Laurent Laveder</li>\n\t<li id="noteid"><sup><a href="#\4">‡</a></sup><span id="\3"> </span><a href="http://www.imdb.com/title/tt0104361">Proof of a cheese moon</a></li>\n\t<li><sup><a href="#\6">‡</a></sup><span id="\5"> </span>After all, things do go <a href="http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident">wrong</a>.</li>\n</ol>"""
        result_re = re.compile(result_pattern, re.U)
        assert_true(result_re.search(html))

    def testEndnotesMalformed(self):
        test = """Scientists say[#lavader] the moon is quite small. But I, for one, don't believe them. Others claim it to be made of cheese[#aardman]. If this proves true I suspect we are in for troubled times[#apollo13!] as people argue over their "share" of the moon's cheese. In the end, its limited size[#lavader] may prove problematic.\n\nnote#unused An unreferenced note.\n\nnote#lavader^ "Proof of the small moon hypothesis":http://antwrp.gsfc.nasa.gov/apod/ap080801.html. Copyright(c) Laurent Laveder\n\nnote#aardman^ "Proof of a cheese moon":http://www.imdb.com/title/tt0104361\n\nnote#apollo13^ After all, things do go "wrong":http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:α!+"""
        html = textile.textile(test)
        result_pattern = r"""^\t<p>Scientists say<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">1</span></a></sup> the moon is quite small. But I, for one, don’t believe them. Others claim it to be made of cheese<sup><a href="#(note[a-f0-9]{32})"><span id="(noteref[a-f0-9]{32})">2</span></a></sup>. If this proves true I suspect we are in for troubled times<sup><span id="(noteref[a-f0-9]{32})">3</span></sup> as people argue over their “share” of the moon’s cheese. In the end, its limited size<sup><a href="#\1"><span id="noteref[a-f0-9]{32}">1</span></a></sup> may prove problematic.</p>\n\n\t<ol style="padding:1em; margin:1em; border-bottom:1px solid gray;">\n\t<li><sup><a href="#\2">α</a></sup><span id="\1"> </span><a href="http://antwrp.gsfc.nasa.gov/apod/ap080801.html">Proof of the small moon hypothesis</a>. Copyright© Laurent Laveder</li>\n\t<li><sup><a href="#\4">α</a></sup><span id="\3"> </span><a href="http://www.imdb.com/title/tt0104361">Proof of a cheese moon</a></li>\n\t<li><sup><a href="#\5">α</a></sup><span id="note[a-f0-9]{32}"> </span>After all, things do go <a href="http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident">wrong</a>.</li>\n\t<li>An unreferenced note.</li>\n</ol>$"""
        result_re = re.compile(result_pattern, re.U)
        assert_true(result_re.search(html))

    def testEndnotesUndefinedNote(self):
        test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!."""
        html = textile.textile(test)
        result_pattern = r"""\t<p>Scientists say the moon is slowly shrinking<sup><a href="#note[a-f0-9]{32}"><span id="noteref[a-f0-9]{32}">1</span></a></sup>.</p>\n\n\t<ol>\n\t<li> Undefined Note \[#my_first_label\].<li>\n</ol>$"""
        result_re = re.compile(result_pattern)
        assert_true(result_re.search(html))

    def testEncodeUrl(self):
        # I tried adding these as doctests, but the unicode tests weren't
        # returning the correct results.
        t = textile.Textile()

        url = 'http://www.example.local'
        result = 'http://www.example.local'
        eurl = t.encode_url(url)
        eq_(eurl, result)

        url = 'http://user@www.example.local'
        result = 'http://user@www.example.local'
        eurl = t.encode_url(url)
        eq_(eurl, result)

        url = 'http://user:password@www.example.local'
        result = 'http://user:password@www.example.local'
        eurl = t.encode_url(url)
        eq_(eurl, result)

        url = 'http://user:password@www.example.local/Ubermensch'
        result = 'http://user:password@www.example.local/Ubermensch'
        eurl = t.encode_url(url)
        eq_(eurl, result)

        url = "http://user:password@www.example.local/Übermensch"
        result = "http://user:password@www.example.local/%C3%9Cbermensch"
        eurl = t.encode_url(url)
        eq_(eurl, result)

        url = 'http://user:password@www.example.local:8080/Übermensch'
        result = 'http://user:password@www.example.local:8080/%C3%9Cbermensch'
        eurl = t.encode_url(url)
        eq_(eurl, result)

    def testFootnoteCrosslink(self):
        html = textile.textile('''See[2] for details, and later, reference it again[2].\n\nfn2^(footy#otherid)[en]. Here are the details.''')
        searchstring = r'\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})"><a href="#fn\1">2</a></sup> for details, and later, reference it again<sup class="footnote"><a href="#fn\1">2</a></sup>.</p>\n\n\t<p class="footy" id="otherid" lang="en"><sup id="fn\1"><a href="#fnrev\1">2</a></sup> Here are the details.</p>$'
        assert_true(re.compile(searchstring).search(html))

    def testFootnoteWithoutReflink(self):
        html = textile.textile('''See[3!] for details.\n\nfn3. Here are the details.''')
        searchstring = r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})">3</sup> for details.</p>\n\n\t<p class="footnote" id="fn\1"><sup>3</sup> Here are the details.</p>$'
        assert_true(re.compile(searchstring).search(html))

    def testSquareBrackets(self):
        html = textile.textile("""1[^st^], 2[^nd^], 3[^rd^]. 2 log[~n~]\n\nA close[!http://textpattern.com/favicon.ico!]image.\nA tight["text":http://textpattern.com/]link.\nA ["footnoted link":http://textpattern.com/][182].""")
        searchstring = r'^\t<p>1<sup>st</sup>, 2<sup>nd</sup>, 3<sup>rd</sup>. 2 log<sub>n</sub></p>\n\n\t<p>A close<img alt="" src="http://textpattern.com/favicon.ico" />image.<br />\nA tight<a href="http://textpattern.com/">text</a>link.<br />\nA <a href="http://textpattern.com/">footnoted link</a><sup class="footnote" id="fnrev([a-f0-9]{32})"><a href="#fn\1">182</a></sup>.</p>'
        assert_true(re.compile(searchstring).search(html))

    def testHTML5(self):
        """docstring for testHTML5"""

        test = 'We use CSS(Cascading Style Sheets).'
        result = '\t<p>We use <abbr title="Cascading Style Sheets"><span class="caps">CSS</span></abbr>.</p>'
        expect = textile.textile(test, html_type="html5")
        eq_(result, expect)


class TestSubclassing():
    """Test Textile subclassing ability."""
    def testChangeGlyphs(self):
        class TextilePL(textile.Textile):
            glyph_definitions = dict(textile.Textile.glyph_definitions,
                quote_double_open = '„'
            )

        test = 'Test "quotes".'
        expect = '\t<p>Test „quotes”.</p>'
        result = TextilePL().parse(test)
        eq_(expect, result)

        # Base Textile is unchanged.
        expect = '\t<p>Test “quotes”.</p>'
        result = textile.textile(test)
        eq_(expect, result)
�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/textilefactory.py�������������������������������������������������������������0000644�0000767�0000024�00000004451�12462764257�020721� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������from __future__ import unicode_literals
from .core import Textile


class TextileFactory(object):
    """
    Use TextileFactory to create a Textile object which can be re-used
    to process multiple strings with the same settings.

    >>> from .tools.doctest_utils import Py3
    >>> f = TextileFactory()
    >>> Py3 << f.process("some text here")
    '\\t<p>some text here</p>'

    >>> f = TextileFactory(restricted=True)
    >>> Py3 << f.process("more text here")
    '\\t<p>more text here</p>'

    Certain parameter values are not permitted because they are illogical:

    >>> f = TextileFactory(lite=True)
    Traceback (most recent call last):
    ...
    ValueError: lite can only be enabled in restricted mode

    >>> f = TextileFactory(head_offset=7)
    Traceback (most recent call last):
    ...
    ValueError: head_offset must be 0-6

    >>> f = TextileFactory(html_type='invalid')
    Traceback (most recent call last):
    ...
    ValueError: html_type must be 'xhtml' or 'html5'


    """

    def __init__(self, restricted=False, lite=False, sanitize=False,
                 noimage=None, auto_link=False, get_sizes=False,
                 head_offset=0, html_type='xhtml'):

        self.class_parms = {}
        self.method_parms = {}

        if lite and not restricted:
            raise ValueError("lite can only be enabled in restricted mode")

        if restricted:
            self.class_parms['restricted'] = True
            self.class_parms['lite'] = lite
            self.method_parms['rel'] = 'nofollow'

        if noimage is None:
            if restricted:
                noimage = True
            else:
                noimage = False

        self.class_parms['noimage'] = noimage
        self.method_parms['sanitize'] = sanitize
        self.class_parms['auto_link'] = auto_link
        self.class_parms['get_sizes'] = get_sizes

        if int(head_offset) not in range(0, 6):
            raise ValueError("head_offset must be 0-6")
        else:
            self.method_parms['head_offset'] = head_offset

        if html_type not in ['xhtml', 'html5']:
            raise ValueError("html_type must be 'xhtml' or 'html5'")
        else:
            self.class_parms['html_type'] = html_type

    def process(self, text):
        return Textile(**self.class_parms).parse(text, **self.method_parms)
�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/tools/������������������������������������������������������������������������0000755�0000767�0000024�00000000000�12462765643�016435� 5����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/tools/__init__.py�������������������������������������������������������������0000644�0000767�0000024�00000000000�12462764257�020534� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/tools/doctest_utils.py��������������������������������������������������������0000644�0000767�0000024�00000006612�12462764257�021701� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������"""
Utilities needed for making doctests compatible with both Py2 and Py3.

Author: Radek Czajka <radekczajka@nowoczesnapolska.org.pl>
"""

from __future__ import unicode_literals
import sys

if sys.version_info[0] < 3:
    import copy

    class Py3Wrapper(object):
        '''
        You can have Python 2 and 3 compatible Unicode-aware code
        without 'u' prefixes (unsupported in Python 3.2) using
        __future__.unicode_literals, but any doctests expecting strings
        will fail in Python 2, because unicode.__repr__ still adds
        a prefix anyway, and bytes.__repr__ doesn't.

        >>> from doctest import run_docstring_examples
        >>> def sad_doctest():
        ...     """
        ...     >>> (b'tuple', 'of', (3, 'things'))
        ...     (b'tuple', 'of', (3, 'things'))
        ...     """
        ...     pass
        >>> run_docstring_examples(sad_doctest, globals())  # doctest: +ELLIPSIS
        ***************...
        Got:
            ('tuple', u'of', (3, u'things'))...

        This class provides a workaround for this issue. 'Shifting'
        an object to Py3 (which is an instance of this class) creates
        a deep copy of it, with all unicode and bytes objects wrapped
        in a class providing a Py3-compatbile __repr__.

        >>> Py3 << (b'tuple', 'of', (3, 'things'))
        (b'tuple', 'of', (3, 'things'))

        '''
        class Py3WrappingMemo(dict):
            """
            The copy.deepcopy function uses one optional argument, which
            is a `memo` dict, used internally as an object cache.
            Normally, deepcopy creates this dict for itself, but we're
            going to use it to modify the behaviour of deepcopy to wrap
            all the unicode and str objects with our wrapper classes.

            This way, deepcopy still behaves as expected if not
            explicitly passed an instance of this class.

            """
            class Py3Unicode(unicode):
                """Wrapper for unicode objects."""
                def __repr__(self):
                    return unicode.__repr__(self)[1:]

            class Py3Str(str):
                """Wrapper for str objects."""
                def __repr__(self):
                    return 'b' + str.__repr__(self)

            # We're meddling with copy.deepcopy internals here.
            # However, iIf deepcopy isn't explicitly passed an instance
            # of this class as `memo`, the only thing this meddling
            # causes is a dict lookup for each unicode and str copied.
            copy._deepcopy_dispatch[unicode] = lambda x, memo: memo.get(x, x)
            copy._deepcopy_dispatch[str] = lambda x, memo: memo.get(x, x)
            copy._deepcopy_dispatch[Py3Unicode] = copy._deepcopy_atomic
            copy._deepcopy_dispatch[Py3Str] = copy._deepcopy_atomic

            def get(self, item, default=None):
                """Actual wrapping happens here."""
                if type(item) is unicode:
                    return self.Py3Unicode(item)
                elif type(item) is str:
                    return self.Py3Str(item)
                else:
                    return dict.get(self, item, default)

        def __lshift__(self, obj):
            return copy.deepcopy(obj, memo=self.Py3WrappingMemo())

else:
    class Py3Wrapper(object):
        """Under Python 3, that's a no-op."""
        def __lshift__(self, obj):
            return obj


Py3 = Py3Wrapper()
����������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/tools/imagesize.py������������������������������������������������������������0000644�0000767�0000024�00000001762�12462764257�020772� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def getimagesize(url):
    """
    Attempts to determine an image's width and height, and returns a tuple,
    (width, height), in pixels or an empty string in case of failure.
    Requires that PIL is installed.

    >>> getimagesize("http://www.google.com/intl/en_ALL/images/logo.gif")
    (276, 110)
    >>> getimagesize("http://bad.domain/")
    ''
    """

    try:
        from PIL import ImageFile
    except ImportError:
        return ''

    try:
        from urllib.request import urlopen
    except (ImportError):
        from urllib2 import urlopen

    try:
        p = ImageFile.Parser()
        f = urlopen(url)
        while True:
            s = f.read(1024)
            if not s:
                break
            p.feed(s)
            if p.image:
                return p.image.size
    except (IOError, ValueError):
        return ''


def setup_module(module):
    from nose.plugins.skip import SkipTest
    try:
        __import__('PIL')
    except ImportError:
        raise SkipTest()
��������������textile-2.2.2/textile/tools/sanitizer.py������������������������������������������������������������0000644�0000767�0000024�00000001155�12462764257�021021� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������def sanitize(string):
    """
    Ensure that the text does not contain any malicious HTML code which might
    break the page.
    """
    try:
        import html5lib
        from html5lib import sanitizer, serializer, treewalkers
    except ImportError:
        raise Exception("html5lib not available")

    p = html5lib.HTMLParser(tokenizer=sanitizer.HTMLSanitizer)
    tree = p.parseFragment(string)

    walker = treewalkers.getTreeWalker("etree")
    stream = walker(tree)

    s = serializer.htmlserializer.HTMLSerializer(omit_optional_tags=False,
            quote_attr_values=True)
    return s.render(stream)
�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile/version.py��������������������������������������������������������������������0000644�0000767�0000024�00000000022�12462764512�017320� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������VERSION = '2.2.2'
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/���������������������������������������������������������������������0000755�0000767�0000024�00000000000�12462765643�016767� 5����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/dependency_links.txt�������������������������������������������������0000644�0000767�0000024�00000000001�12462765643�023035� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������
�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/not-zip-safe���������������������������������������������������������0000644�0000767�0000024�00000000001�12462765643�021215� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������
�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/PKG-INFO�������������������������������������������������������������0000644�0000767�0000024�00000001151�12462765643�020062� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Metadata-Version: 1.1
Name: textile
Version: 2.2.2
Summary: Textile processing for python.
Home-page: http://github.com/textile/python-textile
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description: UNKNOWN
Keywords: textile,text
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/requires.txt���������������������������������������������������������0000644�0000767�0000024�00000000053�12462765643�021365� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������
[:python_version=="2.6"]
ordereddict>=1.1
�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/SOURCES.txt����������������������������������������������������������0000644�0000767�0000024�00000000644�12462765643�020657� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������setup.cfg
setup.py
textile/__init__.py
textile/core.py
textile/textilefactory.py
textile/version.py
textile.egg-info/PKG-INFO
textile.egg-info/SOURCES.txt
textile.egg-info/dependency_links.txt
textile.egg-info/not-zip-safe
textile.egg-info/requires.txt
textile.egg-info/top_level.txt
textile/tests/__init__.py
textile/tools/__init__.py
textile/tools/doctest_utils.py
textile/tools/imagesize.py
textile/tools/sanitizer.py��������������������������������������������������������������������������������������������textile-2.2.2/textile.egg-info/top_level.txt��������������������������������������������������������0000644�0000767�0000024�00000000010�12462765643�021510� 0����������������������������������������������������������������������������������������������������ustar  �dennis��������������������������staff���������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������textile
����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������