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 bs4 import BeautifulSoup
|
||||||
from pymdownx import superfences
|
from pymdownx import superfences
|
||||||
|
|
||||||
|
from judge.markdown_extensions import YouTubeExtension, EmoticonExtension
|
||||||
|
|
||||||
|
|
||||||
EXTENSIONS = [
|
EXTENSIONS = [
|
||||||
"pymdownx.arithmatex",
|
"pymdownx.arithmatex",
|
||||||
|
@ -22,6 +24,8 @@ EXTENSIONS = [
|
||||||
"markdown.extensions.admonition",
|
"markdown.extensions.admonition",
|
||||||
"nl2br",
|
"nl2br",
|
||||||
"mdx_breakless_lists",
|
"mdx_breakless_lists",
|
||||||
|
YouTubeExtension(),
|
||||||
|
EmoticonExtension(),
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTENSION_CONFIGS = {
|
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)
|
|
@ -726,7 +726,7 @@ noscript #noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
.featherlight {
|
.featherlight {
|
||||||
z-index: 1000 !important;
|
z-index: 1001 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @media (max-width: 500px) {
|
// @media (max-width: 500px) {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
.big-emoji {
|
.big-emoji {
|
||||||
font-size: 16px;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
#chat-online {
|
#chat-online {
|
||||||
border-right: 1px solid #ccc;
|
border-right: 1px solid #ccc;
|
||||||
|
|
|
@ -311,17 +311,49 @@ function populateCopyButton() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function register_markdown_editors() {
|
function register_copy_clipboard($elements, callback) {
|
||||||
if (!("Markdown" in window)) {
|
$elements.on('paste', function(event) {
|
||||||
return;
|
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||||
|
for (const index in items) {
|
||||||
|
const item = items[index];
|
||||||
|
if (item.kind === 'file' && item.type.indexOf('image') !== -1) {
|
||||||
|
const blob = item.getAsFile();
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('image', blob);
|
||||||
|
|
||||||
|
$(this).prop('disabled', true);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/pagedown/image-upload/',
|
||||||
|
type: 'POST',
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
success: function(data) {
|
||||||
|
// Assuming the server returns the URL of the image
|
||||||
|
const imageUrl = data.url;
|
||||||
|
const editor = $(event.target); // Get the textarea where the event was triggered
|
||||||
|
let currentMarkdown = editor.val();
|
||||||
|
const markdownImageText = '![](' + imageUrl + ')'; // Markdown for an image
|
||||||
|
|
||||||
|
if (currentMarkdown) currentMarkdown += "\n";
|
||||||
|
currentMarkdown += markdownImageText;
|
||||||
|
|
||||||
|
editor.val(currentMarkdown);
|
||||||
|
callback?.();
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('There was an error uploading the image.');
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
// Re-enable the editor
|
||||||
|
$(this).prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// We only handle the first image in the clipboard data
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$('textarea.wmd-input').each(function() {
|
|
||||||
let id = this.id.substr(9); // remove prefix "wmd-input"
|
|
||||||
var $buttonBar = $(this).prevAll('div[id^="wmd-button-bar"]').first();
|
|
||||||
if (!$buttonBar.length || !$buttonBar.html().trim()) {
|
|
||||||
let converter = new Markdown.Converter();
|
|
||||||
let editor = new Markdown.Editor(converter, id);
|
|
||||||
editor.run();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -399,9 +431,7 @@ function onWindowReady() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
register_copy_clipboard($('textarea.wmd-input'));
|
||||||
register_markdown_editors();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
$('form').submit(function (evt) {
|
$('form').submit(function (evt) {
|
||||||
// Prevent multiple submissions of forms, see #565
|
// Prevent multiple submissions of forms, see #565
|
||||||
|
|
|
@ -4129,17 +4129,20 @@ html .md-typeset .footnote-ref{
|
||||||
.md-typeset :-webkit-any(.emojione,.twemoji,.gemoji){
|
.md-typeset :-webkit-any(.emojione,.twemoji,.gemoji){
|
||||||
display:inline-flex;
|
display:inline-flex;
|
||||||
height:1.125em;
|
height:1.125em;
|
||||||
vertical-align:text-top
|
vertical-align:text-top;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.md-typeset :-moz-any(.emojione,.twemoji,.gemoji){
|
.md-typeset :-moz-any(.emojione,.twemoji,.gemoji){
|
||||||
display:inline-flex;
|
display:inline-flex;
|
||||||
height:1.125em;
|
height:1.125em;
|
||||||
vertical-align:text-top
|
vertical-align:text-top;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.md-typeset :is(.emojione,.twemoji,.gemoji){
|
.md-typeset :is(.emojione,.twemoji,.gemoji){
|
||||||
display:inline-flex;
|
display:inline-flex;
|
||||||
height:1.125em;
|
height:1.125em;
|
||||||
vertical-align:text-top
|
vertical-align:text-top;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.md-typeset :-webkit-any(.emojione,.twemoji,.gemoji) svg{
|
.md-typeset :-webkit-any(.emojione,.twemoji,.gemoji) svg{
|
||||||
fill:currentcolor;
|
fill:currentcolor;
|
||||||
|
@ -4149,12 +4152,14 @@ html .md-typeset .footnote-ref{
|
||||||
.md-typeset :-moz-any(.emojione,.twemoji,.gemoji) svg{
|
.md-typeset :-moz-any(.emojione,.twemoji,.gemoji) svg{
|
||||||
fill:currentcolor;
|
fill:currentcolor;
|
||||||
max-height:100%;
|
max-height:100%;
|
||||||
width:1.125em
|
width:1.125em;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.md-typeset :is(.emojione,.twemoji,.gemoji) svg{
|
.md-typeset :is(.emojione,.twemoji,.gemoji) svg{
|
||||||
fill:currentcolor;
|
fill:currentcolor;
|
||||||
max-height:100%;
|
max-height:100%;
|
||||||
width:1.125em
|
width:1.125em;
|
||||||
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.highlight :-webkit-any(.o,.ow){
|
.highlight :-webkit-any(.o,.ow){
|
||||||
color:var(--md-code-hl-operator-color)
|
color:var(--md-code-hl-operator-color)
|
||||||
|
|
158
resources/pagedown_init.js
Normal file
158
resources/pagedown_init.js
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
var DjangoPagedown = DjangoPagedown | {};
|
||||||
|
|
||||||
|
DjangoPagedown = (function() {
|
||||||
|
var converter = Markdown.getSanitizingConverter();
|
||||||
|
var editors = {};
|
||||||
|
var elements;
|
||||||
|
|
||||||
|
Markdown.Extra.init(converter, {
|
||||||
|
extensions: "all"
|
||||||
|
});
|
||||||
|
|
||||||
|
var createEditor = function(element) {
|
||||||
|
var input = element.getElementsByClassName("wmd-input")[0];
|
||||||
|
if (input === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var id = input.id.substr(9);
|
||||||
|
if (!editors.hasOwnProperty(id)) {
|
||||||
|
var editor = new Markdown.Editor(converter, id, {});
|
||||||
|
|
||||||
|
// Handle image upload
|
||||||
|
if (element.classList.contains("image-upload-enabled")) {
|
||||||
|
var upload = element.getElementsByClassName("pagedown-image-upload")[0];
|
||||||
|
var url = upload.getElementsByClassName("url-input")[0];
|
||||||
|
var file = upload.getElementsByClassName("file-input")[0];
|
||||||
|
var cancel = upload.getElementsByClassName("deletelink")[0];
|
||||||
|
var submit = upload.getElementsByClassName("submit-input")[0];
|
||||||
|
var loading = upload.getElementsByClassName("submit-loading")[0];
|
||||||
|
|
||||||
|
var close = function(value, callback = undefined) {
|
||||||
|
upload.classList.remove("show");
|
||||||
|
url.value = "";
|
||||||
|
file.value = "";
|
||||||
|
document.removeEventListener('click', outsideClickListener);
|
||||||
|
if (callback) callback(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
var outsideClickListener = function(event) {
|
||||||
|
if (!upload.contains(event.target) && upload.classList.contains("show")) {
|
||||||
|
cancel.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
editor.hooks.set("insertImageDialog", function(callback) {
|
||||||
|
upload.classList.add("show");
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
document.addEventListener('click', outsideClickListener);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
cancel.addEventListener(
|
||||||
|
"click",
|
||||||
|
function(event) {
|
||||||
|
close(null, callback);
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
submit.addEventListener(
|
||||||
|
"click",
|
||||||
|
function() {
|
||||||
|
// Regular URL
|
||||||
|
if (url.value.length > 0) {
|
||||||
|
close(url.value, callback);
|
||||||
|
}
|
||||||
|
// File upload
|
||||||
|
else if (file.files.length > 0) {
|
||||||
|
loading.classList.add("show");
|
||||||
|
submit.classList.remove("show");
|
||||||
|
|
||||||
|
var data = new FormData();
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
data.append("image", file.files[0]);
|
||||||
|
xhr.open("POST", file.dataset.action, true);
|
||||||
|
xhr.addEventListener(
|
||||||
|
"load",
|
||||||
|
function() {
|
||||||
|
loading.classList.remove("show");
|
||||||
|
submit.classList.add("show");
|
||||||
|
|
||||||
|
if (xhr.status !== 200) {
|
||||||
|
alert(xhr.statusText);
|
||||||
|
} else {
|
||||||
|
var response = JSON.parse(xhr.response);
|
||||||
|
if (response.success) {
|
||||||
|
close(response.url, callback);
|
||||||
|
} else {
|
||||||
|
if (response.error) {
|
||||||
|
var error = "";
|
||||||
|
for (var key in response.error) {
|
||||||
|
if (response.error.hasOwnProperty(key)) {
|
||||||
|
error += key + ":" + response.error[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
close(null, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
xhr.send(data);
|
||||||
|
} else {
|
||||||
|
// Nothing
|
||||||
|
close(null, callback);
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.run();
|
||||||
|
editors[id] = editor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var destroyEditor = function(element) {
|
||||||
|
var input = element.getElementsByClassName("wmd-input")[0];
|
||||||
|
if (input === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var id = input.id.substr(9);
|
||||||
|
if (editors.hasOwnProperty(id)) {
|
||||||
|
delete editors[id];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var init = function() {
|
||||||
|
elements = document.getElementsByClassName("wmd-wrapper");
|
||||||
|
for (var i = 0; i < elements.length; ++i) {
|
||||||
|
createEditor(elements[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function() {
|
||||||
|
return init();
|
||||||
|
},
|
||||||
|
createEditor: function(element) {
|
||||||
|
return createEditor(element);
|
||||||
|
},
|
||||||
|
destroyEditor: function(element) {
|
||||||
|
return destroyEditor(element);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
window.onload = DjangoPagedown.init;
|
|
@ -585,5 +585,8 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#submit-button').on('click', submit_chat);
|
$('#submit-button').on('click', submit_chat);
|
||||||
|
register_copy_clipboard($("#chat-input"), () => {
|
||||||
|
$('#chat-input').trigger('input');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -211,16 +211,17 @@
|
||||||
$comments.find('a.edit-link').featherlight({
|
$comments.find('a.edit-link').featherlight({
|
||||||
afterOpen: function () {
|
afterOpen: function () {
|
||||||
register_dmmd_preview($('#id-edit-comment-body-preview'));
|
register_dmmd_preview($('#id-edit-comment-body-preview'));
|
||||||
register_markdown_editors();
|
|
||||||
if ('DjangoPagedown' in window) {
|
if ('DjangoPagedown' in window) {
|
||||||
|
DjangoPagedown.createEditor(
|
||||||
|
$('#wmd-input-id-edit-comment-body').closest('.wmd-wrapper')[0]
|
||||||
|
);
|
||||||
|
register_copy_clipboard($('#wmd-input-id-edit-comment-body'));
|
||||||
var $wmd = $('.featherlight .wmd-wrapper');
|
var $wmd = $('.featherlight .wmd-wrapper');
|
||||||
if ($wmd.length) {
|
if ($wmd.length) {
|
||||||
if ('MathJax' in window) {
|
|
||||||
var preview = $('.featherlight div.wmd-preview')[0];
|
var preview = $('.featherlight div.wmd-preview')[0];
|
||||||
renderKatex(preview);
|
renderKatex(preview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
$('#comment-edit').submit(function (event) {
|
$('#comment-edit').submit(function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var id = $('#comment-edit').find('.comment-id').text();
|
var id = $('#comment-edit').find('.comment-id').text();
|
||||||
|
@ -242,6 +243,11 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
beforeClose: function() {
|
||||||
|
DjangoPagedown.destroyEditor(
|
||||||
|
$('#wmd-input-id-edit-comment-body').closest('.wmd-wrapper')[0]
|
||||||
|
);
|
||||||
|
},
|
||||||
variant: 'featherlight-edit'
|
variant: 'featherlight-edit'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
populateCopyButton();
|
populateCopyButton();
|
||||||
},
|
},
|
||||||
error: function(error) {
|
error: function(error) {
|
||||||
alert(error);
|
|
||||||
console.log(error.message);
|
console.log(error.message);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue