class Entities(object): """ Extract backslashes characters; restore with backslashes removed. """ regex = r'\&\w+;' def __init__(self): "This is a thin wrapper for Placeholders" self.placeholders = Placeholders(self.regex, 'en') def insert(self, parts): "Add placeholders." result = self.placeholders.insert(parts) return result def decorate(self, pattern, part_slug=None): """ It's safe HTML, so insert it unchanged. """ return pattern def replace(self, html_parts): "Replace backslashes characters." return self.placeholders.replace(self.decorate, html_parts)
class Urls(object): """ Extract backslashes characters; restore with backslashes removed. """ def __init__(self): "This is a thin wrapper for Placeholders" url_chars = re.escape(r'.:\/_+?&=-#%~') regexps = [ r'https?://[\w%s]+\w' % url_chars, # <-- url r'[\w\.\-\_]+@[\w\.\-\_]+\w' # <-- email ] regexp = '(' + '|'.join(regexps) + ')' self.placeholders = Placeholders(regexp, 'ur') def insert(self, parts): "Add placeholders." return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): "HTML encode the URLs." if email(pattern): link = "mailto:%s" % pattern else: link = pattern html = "<a href=\"%s\" target=\"_blank\">%s</a>" return html % (link, escape(pattern)) def replace(self, html_parts): "Put the URLs back in the finishes text" return self.placeholders.replace(self.decorate, html_parts)
def test_backslashes(): """ Using backslash replacement for testing; there is an actual backslashes class that does this in the wiki. """ parts = {'test': trim(r""" That\'s what it\'s about. """)} marker = 'test' delimiter = '|' # <-- For easy testing. This normally defaults to # chr(30), an invisible 'record separator' in ASCII. def callback(x, _): return x[1:] # <-- Remove leading slash from pattern; # Ignore slug. backslashes = Placeholders(r'\\.', marker, delimiter) tokenized = backslashes.insert(parts) assert {'test': "That|test:1|s what it|test:2|s about."} == tokenized decorated = backslashes.replace(callback, tokenized) assert {'test': "That's what it's about."} == decorated
class CrossReferences(object): """ Manage internal links within a document. """ def __init__(self, parts, outline): """ Create contents list, replace cross-references with placeholders. """ assert isinstance(parts, dict) assert all([isinstance(_, str) for _ in list(parts.keys())]) assert all([isinstance(_, str) for _ in parts]) assert isinstance(outline, Outline) self.regex = r'\@\[[^\]]+\][.,!?;:·]?' self.placeholders = Placeholders(self.regex, 'cr') self.outline = outline def insert(self, parts): """ Use the placeholders object defined in init(). """ return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): """ Find first title that contains ALL terms. """ terms = get_words(pattern) for (numbering, slug, title, title_slug, _) in self.outline: title_terms = get_words(title) # Require all given terms to match the title. # print set(terms), set(title_terms), set(terms) < set(title_terms) if set(terms) <= set(title_terms): section = ".".join(str(i) for i in numbering) slug = section + '_' + slug # leading '-' for short form if pattern[2] == '-': fmt = "<a class=\"unmarked\" href=\"#%s\">§%s</a>" link = fmt % (slug, section) else: fmt = "<a class=\"unmarked\" href=\"#%s\"><i>%s</i> (§%s)</a>" link = fmt % (slug, title.strip(), section) # trailing punctuation if pattern[-1] in ",.?!:;·": link += pattern[-1] return link error = "<kbd class=\"wiki-error\">%s</kbd>" return error % escape(pattern) def replace(self, html_parts): """ Replace each @[...] with a link to that section's #id. """ return self.placeholders.replace(self.decorate, html_parts)
class Citations(object): """ Manage citations of the bibliography. """ def __init__(self, bibliography): """ Create bibliography entries, and setup placeholder identifiers. """ assert isinstance(bibliography, Bibliography) self.bibliography = bibliography regex = r'~\[[^\)]+?\][%s]?' % re.escape(Config.punctuation) self.placeholders = Placeholders(regex, 'citation') self.ibid = None # <-- last bibliography label; @todo add opcit? self.counters = {} # ------------------------------------------------------------ # Insert, decorate and replace make this work as a Placeholder # ------------------------------------------------------------ def insert(self, parts): """ Replace the bibliography brackets with markers, to exclude them from wiki processing. """ return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): """ Format a bibliography bracket into a link to its bibliography entry. """ numbering = self.bibliography.outline.find_numbering(part_slug) citation, note, punctuation = split_pattern(pattern) if self.ibid and slugify(citation) == 'ibid': index = self.bibliography.get_count(part_slug) return self.bibliography.citation(citation, note, punctuation, self.ibid, numbering, index) label = self.bibliography.match(citation) if label: self.ibid = label index = self.bibliography.get_count(part_slug) return self.bibliography.citation(citation, note, punctuation, label, numbering, index) else: self.ibid = None return self.bibliography.outline.error( part_slug, pattern, "Citation not matched in bibliography.") def replace(self, html_parts): """ Replace the placeholders with HTML links. """ return self.placeholders.replace(self.decorate, html_parts)
class Links(object): """ Manage tags appearing in the index. """ def __init__(self, footnotes, prefix=None): """ Create bibliography entries, and setup placeholder identifiers. """ assert isinstance(footnotes, Footnotes) self.footnotes = footnotes # If no prefix, pick a random one. if prefix is not None: self.prefix = prefix else: self.prefix = random_slug('random_') regex = r'\^\[[^\]]+\][.,!?;:·]?' # <-- see decorator self.placeholders = Placeholders(regex, 'link') # ------------------------------------------------------------ # Insert, decorate and replace make this work as a Placeholder # ------------------------------------------------------------ def insert(self, parts): """ Replace the bibliography brackets with markers, to exclude them from wiki processing. """ return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): """ Format a bibliography bracket into a link to its bibliography entry. """ index = self.footnotes.get_count(part_slug) return self.footnotes.footnote(self.prefix, pattern, part_slug, index) def replace(self, html_parts): """ Replace the placeholders with HTML links. """ return self.placeholders.replace(self.decorate, html_parts)
class Verbatim(object): """ Extract {{ verbatim }} blocks; restores as exact text entered; no markup. """ def __init__(self): "This is a thin wrapper for Placeholders" regex = r"{{[^}]*}}" self.placeholders = Placeholders(regex, 'verbatim') def insert(self, parts): "Add placeholders." return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): "Strip braces." trim_brackets = pattern[2:-2] return escape(trim_brackets) def replace(self, html_parts): "Replace verbatim text." return self.placeholders.replace(self.decorate, html_parts)
class Tags(object): """ Manage tags appearing in the index. """ def __init__(self, index): """ Create bibliography entries, and setup placeholder identifiers. """ assert isinstance(index, Index) self.index = index regex = r'[#%%]\[[^\]]+\][%s]?' % re.escape(Config.punctuation) self.placeholders = Placeholders(regex, 'tag') # ------------------------------------------------------------ # Insert, decorate and replace make this work as a Placeholder # ------------------------------------------------------------ def insert(self, parts): """ Replace the bibliography brackets with markers, to exclude them from wiki processing. """ return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): """ Format a bibliography bracket into a link to its bibliography entry. """ numbering = self.index.outline.find_numbering(part_slug) alias, tag, subtag, punctuation = split_pattern(pattern) index = self.index.get_count(part_slug) return self.index.tag(alias, tag, subtag, punctuation, numbering, index) def replace(self, html_parts): """ Replace the placeholders with HTML links. """ return self.placeholders.replace(self.decorate, html_parts)
class Backslashes(object): """ Extract backslashes characters; restore with backslashes removed. """ def __init__(self): "This is a thin wrapper for Placeholders" self.placeholders = Placeholders(r'\\.', 'bs') def insert(self, parts): "Add placeholders." return self.placeholders.insert(parts) def decorate(self, pattern, part_slug=None): """ Strip the leading slash, and HTML encode. This ensures that \< won't allow arbitrary script injection. """ return escape(pattern[1:]) def replace(self, html_parts): "Replace backslashes characters." return self.placeholders.replace(self.decorate, html_parts)
class Demo(object): """ Process DEMO blocks and their arguments. Located here because of interdependency with Wiki(). """ regex = r"DEMO\s+(\([^)]+\)\s)?\s*([%s])\2\2\s*\n.+?\n\2\2\2" % \ re.escape(Config.delimiters) def __init__(self, settings=None): "Just a thin wrapper for Placeholders; parse options in replace()." self.placeholders = Placeholders(self.regex, 'demo') if settings: self.settings = settings.copy() else: self.settings = Settings() self.settings.set('config:user', '_') self.settings.set('config:document', random_slug('demo')) def insert(self, parts): "Add placeholders." return self.placeholders.insert(parts) def decorate(self, pattern, part_slug): """ When we process a new demo block it needs to be assigned a unique id_prefix as its config:document name. Micro chance of a collision; maybe replace with a singleton to track random IDs in use. """ self.settings.set('config:document', random_slug('demo')) wiki = Wiki(self.settings) options = match_demo_options(pattern) fragment = ('index' not in options) part_slug = 'index' if 'index' in options else random_slug('demo-') lines = pattern.splitlines() source = "\n".join(lines[1:-1]) output = wiki.process(None, None, {part_slug: source}, fragment) env = Environment(autoescape=True) if 'wide' in options: tpl = env.from_string( trim(""" <div class="wiki-demo-wide space"> <table> <tbody> <tr> <td> <pre>{{ source|safe }}</pre> </td> </tr> <tr> <td class="wiki-demo-output"> {{ output|safe }} </td> </tr> </tbody> </table> </div> """)) else: tpl = env.from_string( trim(""" <div class="wiki-demo space"> <table> <tbody> <tr> <td width="50%"> <pre>{{ source|safe }}</pre> </td> <td> </td> <td width="48%" class="wiki-demo-output"> {{ output|safe }} </td> </tr> </tbody> </table> </div> """)) return tpl.render(source=escape(source), output=output) def replace(self, html_parts, decorator=None): "Generate demo blocks." if decorator: return self.placeholders.replace(decorator, html_parts) else: return self.placeholders.replace(self.decorate, html_parts)