Implement markdown emoji, youtube, clipboard
This commit is contained in:
parent
5e72b472e6
commit
e923d1b2fe
12 changed files with 381 additions and 27 deletions
|
@ -4,6 +4,8 @@ from django.utils.html import escape
|
|||
from bs4 import BeautifulSoup
|
||||
from pymdownx import superfences
|
||||
|
||||
from judge.markdown_extensions import YouTubeExtension, EmoticonExtension
|
||||
|
||||
|
||||
EXTENSIONS = [
|
||||
"pymdownx.arithmatex",
|
||||
|
@ -22,6 +24,8 @@ EXTENSIONS = [
|
|||
"markdown.extensions.admonition",
|
||||
"nl2br",
|
||||
"mdx_breakless_lists",
|
||||
YouTubeExtension(),
|
||||
EmoticonExtension(),
|
||||
]
|
||||
|
||||
EXTENSION_CONFIGS = {
|
||||
|
|
2
judge/markdown_extensions/__init__.py
Normal file
2
judge/markdown_extensions/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
from .youtube import YouTubeExtension
|
||||
from .emoticon import EmoticonExtension
|
111
judge/markdown_extensions/emoticon.py
Normal file
111
judge/markdown_extensions/emoticon.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
import markdown
|
||||
from markdown.extensions import Extension
|
||||
from markdown.inlinepatterns import InlineProcessor
|
||||
import xml.etree.ElementTree as etree
|
||||
import re
|
||||
|
||||
EMOTICON_EMOJI_MAP = {
|
||||
":D": "\U0001F603", # Smiling Face with Open Mouth
|
||||
":)": "\U0001F642", # Slightly Smiling Face
|
||||
":-)": "\U0001F642", # Slightly Smiling Face with Nose
|
||||
":(": "\U0001F641", # Slightly Frowning Face
|
||||
":-(": "\U0001F641", # Slightly Frowning Face with Nose
|
||||
";)": "\U0001F609", # Winking Face
|
||||
";-)": "\U0001F609", # Winking Face with Nose
|
||||
":P": "\U0001F61B", # Face with Tongue
|
||||
":-P": "\U0001F61B", # Face with Tongue and Nose
|
||||
":p": "\U0001F61B", # Face with Tongue
|
||||
":-p": "\U0001F61B", # Face with Tongue and Nose
|
||||
";P": "\U0001F61C", # Winking Face with Tongue
|
||||
";-P": "\U0001F61C", # Winking Face with Tongue and Nose
|
||||
";p": "\U0001F61C", # Winking Face with Tongue
|
||||
";-p": "\U0001F61C", # Winking Face with Tongue and Nose
|
||||
":'(": "\U0001F622", # Crying Face
|
||||
":o": "\U0001F62E", # Face with Open Mouth
|
||||
":-o": "\U0001F62E", # Face with Open Mouth and Nose
|
||||
":O": "\U0001F62E", # Face with Open Mouth
|
||||
":-O": "\U0001F62E", # Face with Open Mouth and Nose
|
||||
":-0": "\U0001F62E", # Face with Open Mouth and Nose
|
||||
">:(": "\U0001F620", # Angry Face
|
||||
">:-(": "\U0001F620", # Angry Face with Nose
|
||||
">:)": "\U0001F608", # Smiling Face with Horns
|
||||
">:-)": "\U0001F608", # Smiling Face with Horns and Nose
|
||||
"XD": "\U0001F606", # Grinning Squinting Face
|
||||
"xD": "\U0001F606", # Grinning Squinting Face
|
||||
"B)": "\U0001F60E", # Smiling Face with Sunglasses
|
||||
"B-)": "\U0001F60E", # Smiling Face with Sunglasses and Nose
|
||||
"O:)": "\U0001F607", # Smiling Face with Halo
|
||||
"O:-)": "\U0001F607", # Smiling Face with Halo and Nose
|
||||
"0:)": "\U0001F607", # Smiling Face with Halo
|
||||
"0:-)": "\U0001F607", # Smiling Face with Halo and Nose
|
||||
">:P": "\U0001F92A", # Zany Face (sticking out tongue and winking)
|
||||
">:-P": "\U0001F92A", # Zany Face with Nose
|
||||
">:p": "\U0001F92A", # Zany Face (sticking out tongue and winking)
|
||||
">:-p": "\U0001F92A", # Zany Face with Nose
|
||||
":/": "\U0001F615", # Confused Face
|
||||
":-/": "\U0001F615", # Confused Face with Nose
|
||||
":\\": "\U0001F615", # Confused Face
|
||||
":-\\": "\U0001F615", # Confused Face with Nose
|
||||
"3:)": "\U0001F608", # Smiling Face with Horns
|
||||
"3:-)": "\U0001F608", # Smiling Face with Horns and Nose
|
||||
"<3": "\u2764\uFE0F", # Red Heart
|
||||
"</3": "\U0001F494", # Broken Heart
|
||||
":*": "\U0001F618", # Face Blowing a Kiss
|
||||
":-*": "\U0001F618", # Face Blowing a Kiss with Nose
|
||||
";P": "\U0001F61C", # Winking Face with Tongue
|
||||
";-P": "\U0001F61C",
|
||||
">:P": "\U0001F61D", # Face with Stuck-Out Tongue and Tightly-Closed Eyes
|
||||
":-/": "\U0001F615", # Confused Face
|
||||
":/": "\U0001F615",
|
||||
":\\": "\U0001F615",
|
||||
":-\\": "\U0001F615",
|
||||
":|": "\U0001F610", # Neutral Face
|
||||
":-|": "\U0001F610",
|
||||
"8)": "\U0001F60E", # Smiling Face with Sunglasses
|
||||
"8-)": "\U0001F60E",
|
||||
"O:)": "\U0001F607", # Smiling Face with Halo
|
||||
"O:-)": "\U0001F607",
|
||||
":3": "\U0001F60A", # Smiling Face with Smiling Eyes
|
||||
"^.^": "\U0001F60A",
|
||||
"-_-": "\U0001F611", # Expressionless Face
|
||||
"T_T": "\U0001F62D", # Loudly Crying Face
|
||||
"T.T": "\U0001F62D",
|
||||
">.<": "\U0001F623", # Persevering Face
|
||||
"x_x": "\U0001F635", # Dizzy Face
|
||||
"X_X": "\U0001F635",
|
||||
":]": "\U0001F600", # Grinning Face
|
||||
":[": "\U0001F641", # Slightly Frowning Face
|
||||
"=]": "\U0001F600",
|
||||
"=[": "\U0001F641",
|
||||
"D:<": "\U0001F621", # Pouting Face
|
||||
"D:": "\U0001F629", # Weary Face
|
||||
"D=": "\U0001F6AB", # No Entry Sign (sometimes used to denote dismay or frustration)
|
||||
":'D": "\U0001F602", # Face with Tears of Joy
|
||||
"D':": "\U0001F625", # Disappointed but Relieved Face
|
||||
"D8": "\U0001F631", # Face Screaming in Fear
|
||||
"-.-": "\U0001F644", # Face with Rolling Eyes
|
||||
"-_-;": "\U0001F612", # Unamused
|
||||
}
|
||||
|
||||
|
||||
class EmoticonEmojiInlineProcessor(InlineProcessor):
|
||||
def handleMatch(self, m, data):
|
||||
emoticon = m.group(1)
|
||||
emoji = EMOTICON_EMOJI_MAP.get(emoticon, "")
|
||||
if emoji:
|
||||
el = etree.Element("span")
|
||||
el.text = markdown.util.AtomicString(emoji)
|
||||
el.set("class", "big-emoji")
|
||||
return el, m.start(0), m.end(0)
|
||||
else:
|
||||
return None, m.start(0), m.end(0)
|
||||
|
||||
|
||||
class EmoticonExtension(Extension):
|
||||
def extendMarkdown(self, md):
|
||||
# Create the regex pattern to match any emoticon in the map
|
||||
emoticon_pattern = (
|
||||
r"(" + "|".join(map(re.escape, EMOTICON_EMOJI_MAP.keys())) + r")"
|
||||
)
|
||||
emoticon_processor = EmoticonEmojiInlineProcessor(emoticon_pattern, md)
|
||||
md.inlinePatterns.register(emoticon_processor, "emoticon_to_emoji", 1)
|
36
judge/markdown_extensions/youtube.py
Normal file
36
judge/markdown_extensions/youtube.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
import markdown
|
||||
from markdown.inlinepatterns import InlineProcessor
|
||||
from markdown.extensions import Extension
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
YOUTUBE_REGEX = (
|
||||
r"(https?://)?(www\.)?" "(youtube\.com/watch\?v=|youtu\.be/)" "([\w-]+)(&[\w=]*)?"
|
||||
)
|
||||
|
||||
|
||||
class YouTubeEmbedProcessor(InlineProcessor):
|
||||
def handleMatch(self, m, data):
|
||||
youtube_id = m.group(4)
|
||||
if not youtube_id:
|
||||
return None, None, None
|
||||
|
||||
# Create an iframe element with the YouTube embed URL
|
||||
iframe = etree.Element("iframe")
|
||||
iframe.set("width", "100%")
|
||||
iframe.set("height", "360")
|
||||
iframe.set("src", f"https://www.youtube.com/embed/{youtube_id}")
|
||||
iframe.set("frameborder", "0")
|
||||
iframe.set("allowfullscreen", "true")
|
||||
center = etree.Element("center")
|
||||
center.append(iframe)
|
||||
|
||||
# Return the iframe as the element to replace the match, along with the start and end indices
|
||||
return center, m.start(0), m.end(0)
|
||||
|
||||
|
||||
class YouTubeExtension(Extension):
|
||||
def extendMarkdown(self, md):
|
||||
# Create the YouTube link pattern
|
||||
YOUTUBE_PATTERN = YouTubeEmbedProcessor(YOUTUBE_REGEX, md)
|
||||
# Register the pattern to apply the YouTubeEmbedProcessor
|
||||
md.inlinePatterns.register(YOUTUBE_PATTERN, "youtube", 175)
|
Loading…
Add table
Add a link
Reference in a new issue