Override `HTMLExtractor` and create `etree` `Elements` for any elements which should have content parsed as Markdown.
| 35 | |
| 36 | |
| 37 | class HTMLExtractorExtra(HTMLExtractor): |
| 38 | """ |
| 39 | Override `HTMLExtractor` and create `etree` `Elements` for any elements which should have content parsed as |
| 40 | Markdown. |
| 41 | """ |
| 42 | |
| 43 | def __init__(self, md: Markdown, *args, **kwargs): |
| 44 | # All block-level tags. |
| 45 | self.block_level_tags = set(md.block_level_elements.copy()) |
| 46 | # Block-level tags in which the content only gets span level parsing |
| 47 | self.span_tags = set( |
| 48 | ['address', 'dd', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'li', 'p', 'summary', 'td', 'th'] |
| 49 | ) |
| 50 | # Block-level tags which never get their content parsed. |
| 51 | self.raw_tags = set(['canvas', 'math', 'option', 'pre', 'script', 'style', 'textarea']) |
| 52 | |
| 53 | super().__init__(md, *args, **kwargs) |
| 54 | |
| 55 | # Block-level tags in which the content gets parsed as blocks |
| 56 | self.block_tags = set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags) |
| 57 | self.span_and_blocks_tags = self.block_tags | self.span_tags |
| 58 | |
| 59 | def reset(self): |
| 60 | """Reset this instance. Loses all unprocessed data.""" |
| 61 | self.mdstack: list[str] = [] # When markdown=1, stack contains a list of tags |
| 62 | self.treebuilder = etree.TreeBuilder() |
| 63 | self.mdstate: list[Literal['block', 'span', 'off', None]] = [] |
| 64 | self.mdstarted: list[bool] = [] |
| 65 | super().reset() |
| 66 | |
| 67 | def close(self): |
| 68 | """Handle any buffered data.""" |
| 69 | super().close() |
| 70 | # Handle any unclosed tags. |
| 71 | if self.mdstack: |
| 72 | # Close the outermost parent. `handle_endtag` will close all unclosed children. |
| 73 | self.handle_endtag(self.mdstack[0]) |
| 74 | |
| 75 | def get_element(self) -> etree.Element: |
| 76 | """ Return element from `treebuilder` and reset `treebuilder` for later use. """ |
| 77 | element = self.treebuilder.close() |
| 78 | self.treebuilder = etree.TreeBuilder() |
| 79 | return element |
| 80 | |
| 81 | def get_state(self, tag, attrs: Mapping[str, str]) -> Literal['block', 'span', 'off', None]: |
| 82 | """ Return state from tag and `markdown` attribute. One of 'block', 'span', or 'off'. """ |
| 83 | md_attr = attrs.get('markdown', '0') |
| 84 | if md_attr == 'markdown': |
| 85 | # `<tag markdown>` is the same as `<tag markdown='1'>`. |
| 86 | md_attr = '1' |
| 87 | parent_state = self.mdstate[-1] if self.mdstate else None |
| 88 | if parent_state == 'off' or (parent_state == 'span' and md_attr != '0'): |
| 89 | # Only use the parent state if it is more restrictive than the markdown attribute. |
| 90 | md_attr = parent_state |
| 91 | if ((md_attr == '1' and tag in self.block_tags) or |
| 92 | (md_attr == 'block' and tag in self.span_and_blocks_tags)): |
| 93 | return 'block' |
| 94 | elif ((md_attr == '1' and tag in self.span_tags) or |