class BlockQuote(TagDef): tag_name = 'bq' parents = [Optional('root'), Optional('root.p.a8n.fn'), Optional('root.bq.p.a8n.fn')] def render_main(self): return f'<blockquote>{self.content}</blockquote>'
class Emphasis(TagDef): #allow_children = ['a', 'b', 's', 'i'] parents = [Optional('root'), Optional('root.p'), Optional('root.a'), Optional('root.bq')] def render_main(self): return f'<{self.tag_name}>{self.content}</{self.tag_name}>'
class HighLight(TagDef): tag_name = 'hl' parents = [Optional('root'), Optional('root.p'), Optional('root.bq'), Optional('root.bq.p')] def render_main(self): nth_of_type = self.nth_of_type + 1 span_id = f'hlref:{nth_of_type}' return f'<span class="highlight" id="{span_id}">{self.content}</span>'
class Break(TagDef): tag_name = 'br' min_num_text_nodes = 0 max_num_text_nodes = 0 parents = [Optional('root'), Optional('root.p'), Optional('root.bq.p'), Optional('root.bq')] def render_main(self): return '<br/>'
class Paragraph(TagDef): tag_name = 'p' parents = [Optional('root'), Optional('root.bq')] def render_main(self): classes = '' if self.classes: class_str = ' '.join(self.classes) classes = f' class="{class_str}"' return f'<p{classes}>{self.content}</p>'
class URLContext(TagDef): tag_name = 'url' is_context = True min_num_text_nodes = 1 max_num_text_nodes = 1 parents = [ OptionalUnique('root.a'), OptionalUnique('root.p.a'), Optional('root.p.a8n.fn.a'), Optional('root.bq.a8n.fn.a') ]
class Annotation(TagDef): tag_name = 'a8n' parents = [ Optional('root.p'), Optional('root.bq'), Optional('root.bq.p'), Optional('root.ul.li') ] #TODO maybe this can be root level as well? Maybe blockquote too? def render_main(self): nth_of_type = self.nth_of_type + 1 sup_id = f'fnref:{nth_of_type}' href = f'#fn:{nth_of_type}' return f'<span class="annotation__underline">{self.content}</span><sup id="{sup_id}"><a href="{href}" class="footnote-ref" role="doc-noteref">[{nth_of_type}]</a></sup>'
class HorizontalRule(TagDef): tag_name = 'hr' parents = [Optional('root')] #TODO maybe enforce that this can't have any text/children? def render_main(self): return '<hr/>'
class Pre(TagDef): tag_name = 'pre' is_pre = True parents = [Optional('root')] def render_main(self): return f'<pre>{self.content}</pre>'
class FootNote(TagDef): tag_name = 'fn' add_to_collector = True parents = [ Optional('root.p.a8n'), Optional('root.bq.a8n'), Optional('root.bq.p.a8n'), Optional('root.ul.li.a8n') ] def render_main(self): return '' # don't render anything in-place def render_secondary(self): nth_of_type = self.nth_of_type + 1 fn_id = f'fn:{nth_of_type}' href = f'#fnref:{nth_of_type}' return f'<li id="{fn_id}" role="doc-endnote"><p><a href="{href}" class="footnote-backref" role="doc-backlink">↩︎</a> {self.content}</p></li>'
class Table(TagDef): tag_name = 'table' min_num_text_nodes = 0 max_num_text_nodes = 0 parents = [Optional('root')] def render_main(self): return f'<table border="1">{self.content}</table>'
class Anchor(TagDef): tag_name = 'a' parents = [ Optional('root'), Optional('root.p'), Optional('root.p.a8n.fn'), Optional('root.bq.a8n.fn') ] def render_main(self): url = self.context.get('url') if url is None: if self.content.startswith('http'): url = self.content href = f' href="{url}"' if url else '' title = self.context.get('title') title_str = f' title="{title}"' if title else '' return f'<a{href}{title_str}>{self.content}</a>'
class YouTubeEmbed(TagDef): tag_name = 'youtube' min_num_children = 1 max_num_children = 1 parents = [Optional('root')] def render_main(self): video_id = self.content src = f'https://www.youtube.com/embed/{video_id}' width = self.context.get('width') or 560 height = self.context.get('height') or 315 return f'<iframe width="{width}" height="{height}" src="{src}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>'
class Image(TagDef): tag_name = 'img' min_num_text_nodes = 1 max_num_text_nodes = 1 parents = [Optional('root')] def render_main(self): # use first child as src src = self.content attrs = '' for ctx in ('title', 'alt'): ctx_val = self.context.get(ctx) if ctx_val: attrs += f' {ctx}="{ctx_val}"' return f'<img src="{src}"{attrs} />'
class ListItem(TagDef): tag_name = 'li' # allow nesting li 4 levels deep parents = [ Optional('root.ul'), Optional('root.ol'), Optional('root.ul.li'), Optional('root.ul.li.li'), Optional('root.ul.li.li.li'), Optional('root.ol.li'), Optional('root.ol.li.li'), Optional('root.ol.li.li.li'), Optional('root.ol.li.ul'), Optional('root.ul.li.ol'), Optional('root.bq.ul'), Optional('root.bq.ol') ] def render_main(self): return f'<li>{self.content}</li>' def before_render(self): # wrap any adjacent child li in the same type of list as outer list (ul or ol). Makes it easier to create sub-lists parent = self.parent while parent.tag_name == 'li': parent = parent.parent new_children = [] list_wrapper_elem = None for child in self.children: if child.is_element and child.tag_name == 'li': if list_wrapper_elem is None: list_wrapper_def = def_tag_set.get_def(parent.address) # TODO Just use None for num_of_type etc. here... Might be better way to handle appending elements after render list_wrapper_elem = list_wrapper_def( parent.tag_name, f'{self.address}.{parent.tag_name}', None, child.indent_level, self, self.root, None, None, False, False, {}) new_children.append(list_wrapper_elem) list_wrapper_elem.children.append(child) else: list_wrapper_elem = None new_children.append(child) self.children = new_children
class UnorderedList(List): tag_name = 'ul' parents = [Optional('root'), Optional('root.ol.li'), Optional('root.bq')]
class Headline(TagDef): parents = [Optional('root')] def render_main(self): return f'<{self.tag_name}>{self.content}</{self.tag_name}>'
class OrderedList(List): tag_name = 'ol' parents = [Optional('root'), Optional('root.ul.li'), Optional('root.bq')]