from .babycode_parser import Parser from markupsafe import escape import re BABYCODE_VERSION = 2 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 is_tag(e, tag): if e is None: return False if isinstance(e, str): return False if e['type'] != 'bbcode': return False return e['name'] == tag def is_text(e): return isinstance(e, str) def tag_code(children, attr, surrounding): is_inline = children.find('\n') == -1 if is_inline: return f"{children}" else: t = children.strip() button = f"" return f"
{button}{t}
" def tag_list(children): list_body = re.sub(r" +\n", "
", children.strip()) list_body = re.sub(r"\n\n+", "\1", list_body) return " ".join([f"
  • {x}
  • " for x in list_body.split("\1") if x]) def tag_color(children, attr, surrounding): hex_re = r"^#?([0-9a-f]{6}|[0-9a-f]{3})$" potential_color = attr.lower().strip() if potential_color in NAMED_COLORS: return f"{children}" m = re.match(hex_re, potential_color) if m: return f"{children}" # return just the way it was if we can't parse it return f"[color={attr}]{children}[/color]" def tag_spoiler(children, attr, surrounding): spoiler_name = attr if attr else "Spoiler" content = f"" container = f"""""" return container def tag_image(children, attr, surrounding): img = f"\"{children}\"" if not is_tag(surrounding[0], 'img'): img = f"
    {img}" if not is_tag(surrounding[1], 'img'): img = f"{img}
    " return img TAGS = { "b": lambda children, attr, _: f"{children}", "i": lambda children, attr, _: f"{children}", "s": lambda children, attr, _: f"{children}", "u": lambda children, attr, _: f"{children}", "img": tag_image, "url": lambda children, attr, _: f"{children}", "quote": lambda children, attr, _: f"
    {children}
    ", "code": tag_code, "ul": lambda children, attr, _: f"", "ol": lambda children, attr, _: f"
      {tag_list(children)}
    ", "big": lambda children, attr, _: f"{children}", "small": lambda children, attr, _: f"{children}", "color": tag_color, "center": lambda children, attr, _: f"
    {children}
    ", "right": lambda children, attr, _: f"
    {children}
    ", "spoiler": tag_spoiler, } def make_emoji(name, code): return f' {name}' 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", "
    ", text) text = re.sub(r"\n\n+", "

    ", 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() out = "" def fold(element, nobr, surrounding): if isinstance(element, str): if nobr: return element return break_lines(element) match element['type']: case "bbcode": c = "" for i in range(len(element['children'])): child = element['children'][i] _surrounding = ( element['children'][i - 1] if i-1 >= 0 else None, element['children'][i + 1] if i+1 < len(element['children']) else None ) _nobr = element['name'] == "code" or element['name'] == "ul" or element['name'] == "ol" c = c + fold(child, _nobr, _surrounding) res = TAGS[element['name']](c, element['attr'], surrounding) return res case "link": return f"{element['url']}" case 'emote': return EMOJI[element['name']] case "rule": return "
    " # for e in elements: # out = out + fold(e, False) for i in range(len(elements)): e = elements[i] surrounding = ( elements[i - 1] if i-1 >= 0 else None, elements[i + 1] if i+1 < len(elements) else None ) out = out + fold(e, False, surrounding) return out