test bkp
This commit is contained in:
1 parent
6ccab0347b
commit
954a2fd6d6
18684 files changed
+601012
-190
No files matched your search
@@ -0,0 +1,219 @@
|
||||
"""HTML."""
|
||||
import xml.etree.ElementTree as etree
|
||||
from .block import Block, type_string_in
|
||||
from ..blocks import BlocksExtension
|
||||
import re
|
||||
|
||||
# Sub-patterns parts
|
||||
# Whitespace
|
||||
WS = r'(?:[ \t])'
|
||||
# CSS escapes
|
||||
CSS_ESCAPES = fr'(?:\\(?:[a-f0-9]{{1,6}}{WS}?|[^\r\n\f]|$))'
|
||||
# CSS Identifier
|
||||
IDENTIFIER = r'''
|
||||
(?:(?:-?(?:[^\x00-\x2f\x30-\x40\x5B-\x5E\x60\x7B-\x9f])+|--)
|
||||
(?:[^\x00-\x2c\x2e\x2f\x3A-\x40\x5B-\x5E\x60\x7B-\x9f])*)
|
||||
'''
|
||||
# Value: quoted string or identifier
|
||||
VALUE = r'''
|
||||
(?:"(?:\\(?:.)|[^\\"\r\n\f]+)*?"|'(?:\\(?:.)|[^\\'\r\n\f]+)*?'|{ident}+)
|
||||
'''.format(ident=IDENTIFIER)
|
||||
# Attribute value comparison.
|
||||
ATTR = r'''
|
||||
(?:{ws}*(?P<cmp>=){ws}*(?P<value>{value}))?
|
||||
'''.format(ws=WS, value=VALUE)
|
||||
# Selector patterns
|
||||
# IDs (`#id`)
|
||||
PAT_ID = fr'\#{IDENTIFIER}'
|
||||
# Classes (`.class`)
|
||||
PAT_CLASS = fr'\.{IDENTIFIER}'
|
||||
# Attributes (`[attr]`, `[attr=value]`, etc.)
|
||||
PAT_ATTR = r'''
|
||||
\[(?:{ws}*(?P<attr_name>{ident}){attr})+{ws}*\]
|
||||
'''.format(ws=WS, ident=IDENTIFIER, attr=ATTR)
|
||||
|
||||
RE_IDENT = re.compile(IDENTIFIER, flags=re.I | re.X)
|
||||
RE_ID = re.compile(PAT_ID, flags=re.I | re.X)
|
||||
RE_CLASS = re.compile(PAT_CLASS, flags=re.I | re.X)
|
||||
RE_ATTRS = re.compile(PAT_ATTR, flags=re.I | re.X)
|
||||
RE_ATTR = re.compile(fr'(?P<attr_name>{IDENTIFIER}){ATTR}', flags=re.I | re.X)
|
||||
|
||||
ATTRIBUTES = {'id': RE_ID, 'class': RE_CLASS, 'attr': RE_ATTRS}
|
||||
VALID_MODES = {'auto', 'inline', 'block', 'raw', 'html'}
|
||||
|
||||
|
||||
def parse_selectors(selector, require_tag=True):
|
||||
"""Parse the selector."""
|
||||
|
||||
eol = len(selector)
|
||||
tag = None
|
||||
attrs = {}
|
||||
end = 0
|
||||
m = None
|
||||
|
||||
if require_tag:
|
||||
m = RE_IDENT.match(selector)
|
||||
if m is None:
|
||||
raise ValueError('No defined tag')
|
||||
tag = m.group(0)
|
||||
end = m.end()
|
||||
|
||||
found_id = False
|
||||
while end < eol:
|
||||
for atype, pat in ATTRIBUTES.items():
|
||||
m = pat.match(selector, end)
|
||||
if m is not None:
|
||||
if atype == 'id':
|
||||
if not found_id:
|
||||
attrs[atype] = m.group(0)[1:]
|
||||
end = m.end()
|
||||
found_id = True
|
||||
else:
|
||||
raise ValueError('Only one ID is allowed')
|
||||
elif atype == 'class':
|
||||
if atype not in attrs:
|
||||
attrs[atype] = [m.group(0)[1:]]
|
||||
else:
|
||||
attrs[atype].append(m.group(0)[1:])
|
||||
end = m.end()
|
||||
else:
|
||||
results = m.group(0)
|
||||
m2 = RE_ATTR.search(results)
|
||||
while m2 is not None:
|
||||
pos = m2.end()
|
||||
name = m2.group('attr_name').lower()
|
||||
value = m2.group('value')
|
||||
if value is None:
|
||||
value = name if name != 'class' else ''
|
||||
elif value.startswith(('"', "'")):
|
||||
value = value[1:-1]
|
||||
|
||||
if name == 'class':
|
||||
value = [v for v in value.split(' ') if v]
|
||||
if value:
|
||||
if name in attrs:
|
||||
attrs[name].extend(value)
|
||||
else:
|
||||
attrs[name] = value
|
||||
else:
|
||||
value = value
|
||||
attrs[name] = value
|
||||
m2 = RE_ATTR.search(results, pos)
|
||||
end = m.end()
|
||||
break
|
||||
|
||||
if m is None:
|
||||
raise ValueError('Invalid selector')
|
||||
|
||||
if 'class' in attrs:
|
||||
attrs['class'] = ' '.join(sorted(attrs['class']))
|
||||
|
||||
return tag, attrs
|
||||
|
||||
|
||||
class HTML(Block):
|
||||
"""
|
||||
HTML.
|
||||
|
||||
Arguments (1 required):
|
||||
- HTML tag name
|
||||
|
||||
Options:
|
||||
- `markdown` (string): specify how content inside the element should be treated:
|
||||
- `auto`: will automatically determine how an element's content should be handled.
|
||||
- `inline`: treat content as an inline element's content.
|
||||
- `block`: treat content as a block element's content.
|
||||
- `raw`: treat the content as raw content (atomic).
|
||||
|
||||
Content:
|
||||
HTML element content.
|
||||
"""
|
||||
|
||||
NAME = 'html'
|
||||
ARGUMENT = True
|
||||
OPTIONS = {
|
||||
'markdown': ('auto', type_string_in(VALID_MODES))
|
||||
}
|
||||
|
||||
def __init__(self, length, tracker, md, config):
|
||||
"""Initialize."""
|
||||
|
||||
self.markdown = None
|
||||
self.custom = {}
|
||||
for entry in config.get('custom'):
|
||||
mode = entry.get('mode', 'auto')
|
||||
self.custom[entry['tag']] = mode if mode in VALID_MODES else 'auto'
|
||||
super().__init__(length, tracker, md, config)
|
||||
|
||||
def on_validate(self, parent):
|
||||
"""Handle argument parsing."""
|
||||
|
||||
try:
|
||||
self.tag, self.attr = parse_selectors(self.argument)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def on_markdown(self):
|
||||
"""Check if this is atomic."""
|
||||
|
||||
mode = self.options['markdown']
|
||||
if mode == 'auto':
|
||||
tag = self.tag.lower()
|
||||
mode = self.custom.get(tag, mode)
|
||||
|
||||
if mode == 'html':
|
||||
mode = 'raw'
|
||||
return mode
|
||||
|
||||
def on_create(self, parent):
|
||||
"""Create the element."""
|
||||
|
||||
# Create element
|
||||
return etree.SubElement(parent, self.tag.lower(), self.attr)
|
||||
|
||||
def is_html(self, tag):
|
||||
"""Does tag require no processing and no HTML escaping."""
|
||||
|
||||
return tag.tag in ('script', 'style')
|
||||
|
||||
def on_end(self, block):
|
||||
"""On end event."""
|
||||
|
||||
mode = self.options['markdown']
|
||||
if mode == 'auto':
|
||||
tag = self.tag.lower()
|
||||
mode = self.custom.get(tag, mode)
|
||||
|
||||
if (mode == 'auto' and self.is_html(block)) or mode == 'html':
|
||||
block.text = self.md.htmlStash.store(block.text)
|
||||
elif (mode == 'auto' and self.is_raw(block)) or mode == 'raw':
|
||||
block.text = self.md.htmlStash.store(self.html_escape(block.text))
|
||||
|
||||
|
||||
class HTMLExtension(BlocksExtension):
|
||||
"""HTML Blocks Extension."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize."""
|
||||
|
||||
self.config = {
|
||||
"custom": [
|
||||
[],
|
||||
"Specify handling for custom blocks."
|
||||
]
|
||||
}
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def extendMarkdownBlocks(self, md, block_mgr):
|
||||
"""Extend Markdown blocks."""
|
||||
|
||||
block_mgr.register(HTML, self.getConfigs())
|
||||
|
||||
|
||||
def makeExtension(*args, **kwargs):
|
||||
"""Return extension."""
|
||||
|
||||
return HTMLExtension(*args, **kwargs)
|
||||
Reference in new issue
Block a user