174 lines
6.8 KiB
Python
174 lines
6.8 KiB
Python
from .babycode_parser import Parser
|
|
from markupsafe import escape
|
|
import re
|
|
|
|
NAMED_COLORS = [
|
|
'black', 'silver', 'gray', 'white', 'maroon', 'red',
|
|
'purple', 'fuchsia', 'green', 'lime', 'olive', 'yellow',
|
|
'navy', 'blue', 'teal', 'aqua', 'aliceblue', 'antiquewhite',
|
|
'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black',
|
|
'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue',
|
|
'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
|
|
'cyan', 'aqua', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
|
|
'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange',
|
|
'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
|
|
'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray',
|
|
'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia',
|
|
'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green',
|
|
'greenyellow', 'grey', 'gray', 'honeydew', 'hotpink', 'indianred',
|
|
'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
|
|
'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray',
|
|
'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
|
|
'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen',
|
|
'linen', 'magenta', 'fuchsia', 'maroon', 'mediumaquamarine', 'mediumblue',
|
|
'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise',
|
|
'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite',
|
|
'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered',
|
|
'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
|
|
'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple',
|
|
'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon',
|
|
'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue',
|
|
'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue',
|
|
'tan', 'teal', 'thistle', 'tomato', 'transparent', 'turquoise',
|
|
'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen',
|
|
]
|
|
|
|
def tag_code(children, attr):
|
|
is_inline = children.find('\n') == -1
|
|
if is_inline:
|
|
return f"<code class=\"inline-code\">{children}</code>"
|
|
else:
|
|
t = children.strip()
|
|
button = f"<button type=button class=\"copy-code\" value=\"{t}\">Copy</button>"
|
|
return f"<pre><span class=\"copy-code-container\">{button}</span><code>{t}</code></pre>"
|
|
|
|
def tag_list(children):
|
|
list_body = re.sub(r" +\n", "<br>", children.strip())
|
|
list_body = re.sub(r"\n\n+", "\1", list_body)
|
|
return " ".join([f"<li>{x}</li>" for x in list_body.split("\1") if x])
|
|
|
|
def tag_color(children, attr):
|
|
hex_re = r"^#?([0-9a-f]{6}|[0-9a-f]{3})$"
|
|
potential_color = attr.lower().strip()
|
|
|
|
if potential_color in NAMED_COLORS:
|
|
return f"<span style='color: {potential_color};'>{children}</span>"
|
|
|
|
m = re.match(hex_re, potential_color)
|
|
if m:
|
|
return f"<span style='color: #{m.group(1)};'>{children}</span>"
|
|
|
|
# return just the way it was if we can't parse it
|
|
return f"[color={attr}]{children}[/color]"
|
|
|
|
TAGS = {
|
|
"b": lambda children, attr: f"<strong>{children}</strong>",
|
|
"i": lambda children, attr: f"<em>{children}</em>",
|
|
"s": lambda children, attr: f"<del>{children}</del>",
|
|
"u": lambda children, attr: f"<u>{children}</u>",
|
|
|
|
"img": lambda children, attr: f"<div class=\"post-img-container\"><img class=\"block-img\" src=\"{attr}\" alt=\"{children}\"></div>",
|
|
"url": lambda children, attr: f"<a href={attr}>{children}</a>",
|
|
"quote": lambda children, attr: f"<blockquote>{children}</blockquote>",
|
|
"code": tag_code,
|
|
"ul": lambda children, attr: f"<ul>{tag_list(children)}</ul>",
|
|
"ol": lambda children, attr: f"<ol>{tag_list(children)}</ol>",
|
|
|
|
"big": lambda children, attr: f"<span style='font-size: 2rem;'>{children}</span>",
|
|
"small": lambda children, attr: f"<span style='font-size: 0.75rem;'>{children}</span>",
|
|
"color": tag_color,
|
|
|
|
"center": lambda children, attr: f"<div style='text-align: center;'>{children}</div>",
|
|
"right": lambda children, attr: f"<div style='text-align: right;'>{children}</div>",
|
|
}
|
|
|
|
def make_emoji(name, code):
|
|
return f' <img class=emoji src="/static/emoji/{name}.png" alt="{name}" title=":{code}:">'
|
|
|
|
EMOJI = {
|
|
'angry': make_emoji('angry', 'angry'),
|
|
|
|
'(': make_emoji('frown', '('),
|
|
|
|
'D': make_emoji('grin', 'D'),
|
|
|
|
'imp': make_emoji('imp', 'imp'),
|
|
|
|
'angryimp': make_emoji('impangry', 'angryimp'),
|
|
'impangry': make_emoji('impangry', 'impangry'),
|
|
|
|
'lobster': make_emoji('lobster', 'lobster'),
|
|
|
|
'|': make_emoji('neutral', '|'),
|
|
|
|
'pensive': make_emoji('pensive', 'pensive'),
|
|
|
|
'scissors': make_emoji('scissors', 'scissors'),
|
|
|
|
')': make_emoji('smile', ')'),
|
|
|
|
'smiletear': make_emoji('smiletear', 'smiletear'),
|
|
'crytear': make_emoji('smiletear', 'crytear'),
|
|
|
|
',': make_emoji('sob', ','),
|
|
'T': make_emoji('sob', 'T'),
|
|
'cry': make_emoji('sob', 'cry'),
|
|
'sob': make_emoji('sob', 'sob'),
|
|
|
|
'o': make_emoji('surprised', 'o'),
|
|
'O': make_emoji('surprised', 'O'),
|
|
|
|
'hmm': make_emoji('think', 'hmm'),
|
|
'think': make_emoji('think', 'think'),
|
|
'thinking': make_emoji('think', 'thinking'),
|
|
|
|
'P': make_emoji('tongue', 'P'),
|
|
'p': make_emoji('tongue', 'p'),
|
|
|
|
'weary': make_emoji('weary', 'weary'),
|
|
|
|
';': make_emoji('wink', ';'),
|
|
'wink': make_emoji('wink', 'wink'),
|
|
}
|
|
|
|
TEXT_ONLY = ["code"]
|
|
|
|
def break_lines(text):
|
|
text = re.sub(r" +\n", "<br>", text)
|
|
text = re.sub(r"\n\n+", "<br><br>", text)
|
|
return text
|
|
|
|
def babycode_to_html(s):
|
|
subj = escape(s.strip().replace('\r\n', '\n').replace('\r', '\n'))
|
|
parser = Parser(subj)
|
|
parser.valid_bbcode_tags = TAGS.keys()
|
|
parser.bbcode_tags_only_text_children = TEXT_ONLY
|
|
parser.valid_emotes = EMOJI.keys()
|
|
|
|
elements = parser.parse()
|
|
print(elements)
|
|
out = ""
|
|
def fold(element, nobr):
|
|
if isinstance(element, str):
|
|
if nobr:
|
|
return element
|
|
return break_lines(element)
|
|
|
|
match element['type']:
|
|
case "bbcode":
|
|
c = ""
|
|
for child in element['children']:
|
|
_nobr = element['name'] == "code" or element['name'] == "ul" or element['name'] == "ol"
|
|
c = c + fold(child, _nobr)
|
|
res = TAGS[element['name']](c, element['attr'])
|
|
return res
|
|
case "link":
|
|
return f"<a href=\"{element['url']}\">{element['url']}</a>"
|
|
case 'emote':
|
|
return EMOJI[element['name']]
|
|
case "rule":
|
|
return "<hr>"
|
|
for e in elements:
|
|
out = out + fold(e, False)
|
|
return out
|