Change pre code css in markdown
This commit is contained in:
parent
d75a498d18
commit
aef795b40c
24 changed files with 200 additions and 397 deletions
|
@ -130,7 +130,6 @@ USE_SELENIUM = False
|
||||||
SELENIUM_CUSTOM_CHROME_PATH = None
|
SELENIUM_CUSTOM_CHROME_PATH = None
|
||||||
SELENIUM_CHROMEDRIVER_PATH = "chromedriver"
|
SELENIUM_CHROMEDRIVER_PATH = "chromedriver"
|
||||||
|
|
||||||
PYGMENT_THEME = "pygment-github.css"
|
|
||||||
INLINE_JQUERY = True
|
INLINE_JQUERY = True
|
||||||
INLINE_FONTAWESOME = True
|
INLINE_FONTAWESOME = True
|
||||||
JQUERY_JS = "//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"
|
JQUERY_JS = "//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"
|
||||||
|
|
|
@ -1,44 +1,13 @@
|
||||||
from django.utils.html import escape, mark_safe
|
from django.utils.html import escape, mark_safe
|
||||||
|
from judge.markdown import markdown
|
||||||
|
|
||||||
__all__ = ["highlight_code"]
|
__all__ = ["highlight_code"]
|
||||||
|
|
||||||
|
|
||||||
def _make_pre_code(code):
|
def highlight_code(code, language, linenos=True, title=None):
|
||||||
return mark_safe("<pre>" + escape(code) + "</pre>")
|
linenos_option = 'linenums="1"' if linenos else ""
|
||||||
|
title_option = f'title="{title}"' if title else ""
|
||||||
|
options = f"{{.{language} {linenos_option} {title_option}}}"
|
||||||
|
|
||||||
|
value = f"```{options}\n{code}\n```\n"
|
||||||
try:
|
return mark_safe(markdown(value))
|
||||||
import pygments
|
|
||||||
import pygments.lexers
|
|
||||||
import pygments.formatters
|
|
||||||
import pygments.util
|
|
||||||
except ImportError:
|
|
||||||
|
|
||||||
def highlight_code(code, language, cssclass=None):
|
|
||||||
return _make_pre_code(code)
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
def highlight_code(code, language, cssclass="codehilite", linenos=True):
|
|
||||||
try:
|
|
||||||
lexer = pygments.lexers.get_lexer_by_name(language)
|
|
||||||
except pygments.util.ClassNotFound:
|
|
||||||
return _make_pre_code(code)
|
|
||||||
|
|
||||||
if linenos:
|
|
||||||
return mark_safe(
|
|
||||||
pygments.highlight(
|
|
||||||
code,
|
|
||||||
lexer,
|
|
||||||
pygments.formatters.HtmlFormatter(
|
|
||||||
cssclass=cssclass, linenos="table", wrapcode=True
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return mark_safe(
|
|
||||||
pygments.highlight(
|
|
||||||
code,
|
|
||||||
lexer,
|
|
||||||
pygments.formatters.HtmlFormatter(cssclass=cssclass, wrapcode=True),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,112 +1,7 @@
|
||||||
from .. import registry
|
from .. import registry
|
||||||
import markdown as _markdown
|
from judge.markdown import markdown as _markdown
|
||||||
import bleach
|
|
||||||
from django.utils.html import escape
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
from pymdownx import superfences
|
|
||||||
|
|
||||||
|
|
||||||
EXTENSIONS = [
|
|
||||||
"pymdownx.arithmatex",
|
|
||||||
"pymdownx.magiclink",
|
|
||||||
"pymdownx.betterem",
|
|
||||||
"pymdownx.details",
|
|
||||||
"pymdownx.emoji",
|
|
||||||
"pymdownx.inlinehilite",
|
|
||||||
"pymdownx.superfences",
|
|
||||||
"pymdownx.tasklist",
|
|
||||||
"markdown.extensions.footnotes",
|
|
||||||
"markdown.extensions.attr_list",
|
|
||||||
"markdown.extensions.def_list",
|
|
||||||
"markdown.extensions.tables",
|
|
||||||
"markdown.extensions.admonition",
|
|
||||||
"nl2br",
|
|
||||||
"mdx_breakless_lists",
|
|
||||||
]
|
|
||||||
|
|
||||||
EXTENSION_CONFIGS = {
|
|
||||||
"pymdownx.superfences": {
|
|
||||||
"custom_fences": [
|
|
||||||
{
|
|
||||||
"name": "sample",
|
|
||||||
"class": "no-border",
|
|
||||||
"format": superfences.fence_code_format,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [
|
|
||||||
"img",
|
|
||||||
"center",
|
|
||||||
"iframe",
|
|
||||||
"div",
|
|
||||||
"span",
|
|
||||||
"table",
|
|
||||||
"tr",
|
|
||||||
"td",
|
|
||||||
"th",
|
|
||||||
"tr",
|
|
||||||
"pre",
|
|
||||||
"code",
|
|
||||||
"p",
|
|
||||||
"hr",
|
|
||||||
"h1",
|
|
||||||
"h2",
|
|
||||||
"h3",
|
|
||||||
"h4",
|
|
||||||
"h5",
|
|
||||||
"h6",
|
|
||||||
"thead",
|
|
||||||
"tbody",
|
|
||||||
"sup",
|
|
||||||
"dl",
|
|
||||||
"dt",
|
|
||||||
"dd",
|
|
||||||
"br",
|
|
||||||
"details",
|
|
||||||
"summary",
|
|
||||||
]
|
|
||||||
|
|
||||||
ALLOWED_ATTRS = ["src", "width", "height", "href", "class", "open"]
|
|
||||||
|
|
||||||
|
|
||||||
@registry.filter
|
@registry.filter
|
||||||
def markdown(value, lazy_load=False):
|
def markdown(value, lazy_load=False):
|
||||||
extensions = EXTENSIONS
|
return _markdown(value, lazy_load)
|
||||||
html = _markdown.markdown(
|
|
||||||
value, extensions=extensions, extension_configs=EXTENSION_CONFIGS
|
|
||||||
)
|
|
||||||
|
|
||||||
# Don't clean mathjax
|
|
||||||
hash_script_tag = {}
|
|
||||||
soup = BeautifulSoup(html, "html.parser")
|
|
||||||
for script_tag in soup.find_all("script"):
|
|
||||||
allow_math_types = ["math/tex", "math/tex; mode=display"]
|
|
||||||
if script_tag.attrs.get("type", False) in allow_math_types:
|
|
||||||
hash_script_tag[str(hash(str(script_tag)))] = str(script_tag)
|
|
||||||
|
|
||||||
for hashed_tag in hash_script_tag:
|
|
||||||
tag = hash_script_tag[hashed_tag]
|
|
||||||
html = html.replace(tag, hashed_tag)
|
|
||||||
|
|
||||||
html = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS)
|
|
||||||
|
|
||||||
for hashed_tag in hash_script_tag:
|
|
||||||
tag = hash_script_tag[hashed_tag]
|
|
||||||
html = html.replace(hashed_tag, tag)
|
|
||||||
|
|
||||||
if not html:
|
|
||||||
html = escape(value)
|
|
||||||
if lazy_load:
|
|
||||||
soup = BeautifulSoup(html, features="html.parser")
|
|
||||||
for img in soup.findAll("img"):
|
|
||||||
if img.get("src"):
|
|
||||||
img["data-src"] = img["src"]
|
|
||||||
img["src"] = ""
|
|
||||||
for img in soup.findAll("iframe"):
|
|
||||||
if img.get("src"):
|
|
||||||
img["data-src"] = img["src"]
|
|
||||||
img["src"] = ""
|
|
||||||
html = str(soup)
|
|
||||||
return '<div class="md-typeset">%s</div>' % html
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ class Command(BaseCommand):
|
||||||
.replace("'//", "'https://")
|
.replace("'//", "'https://")
|
||||||
)
|
)
|
||||||
maker.title = problem_name
|
maker.title = problem_name
|
||||||
for file in ("style.css", "pygment-github.css", "mathjax3_config.js"):
|
for file in ("style.css", "mathjax3_config.js"):
|
||||||
maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file))
|
maker.load(file, os.path.join(settings.DMOJ_RESOURCES, file))
|
||||||
maker.make(debug=True)
|
maker.make(debug=True)
|
||||||
if not maker.success:
|
if not maker.success:
|
||||||
|
|
117
judge/markdown.py
Normal file
117
judge/markdown.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import markdown as _markdown
|
||||||
|
import bleach
|
||||||
|
from django.utils.html import escape
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from pymdownx import superfences
|
||||||
|
|
||||||
|
|
||||||
|
EXTENSIONS = [
|
||||||
|
"pymdownx.arithmatex",
|
||||||
|
"pymdownx.magiclink",
|
||||||
|
"pymdownx.betterem",
|
||||||
|
"pymdownx.details",
|
||||||
|
"pymdownx.emoji",
|
||||||
|
"pymdownx.inlinehilite",
|
||||||
|
"pymdownx.superfences",
|
||||||
|
"pymdownx.highlight",
|
||||||
|
"pymdownx.tasklist",
|
||||||
|
"markdown.extensions.footnotes",
|
||||||
|
"markdown.extensions.attr_list",
|
||||||
|
"markdown.extensions.def_list",
|
||||||
|
"markdown.extensions.tables",
|
||||||
|
"markdown.extensions.admonition",
|
||||||
|
"nl2br",
|
||||||
|
"mdx_breakless_lists",
|
||||||
|
]
|
||||||
|
|
||||||
|
EXTENSION_CONFIGS = {
|
||||||
|
"pymdownx.superfences": {
|
||||||
|
"custom_fences": [
|
||||||
|
{
|
||||||
|
"name": "sample",
|
||||||
|
"class": "no-border",
|
||||||
|
"format": superfences.fence_code_format,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"pymdownx.highlight": {
|
||||||
|
"auto_title": True,
|
||||||
|
"auto_title_map": {
|
||||||
|
"Text Only": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + [
|
||||||
|
"img",
|
||||||
|
"center",
|
||||||
|
"iframe",
|
||||||
|
"div",
|
||||||
|
"span",
|
||||||
|
"table",
|
||||||
|
"tr",
|
||||||
|
"td",
|
||||||
|
"th",
|
||||||
|
"tr",
|
||||||
|
"pre",
|
||||||
|
"code",
|
||||||
|
"p",
|
||||||
|
"hr",
|
||||||
|
"h1",
|
||||||
|
"h2",
|
||||||
|
"h3",
|
||||||
|
"h4",
|
||||||
|
"h5",
|
||||||
|
"h6",
|
||||||
|
"thead",
|
||||||
|
"tbody",
|
||||||
|
"sup",
|
||||||
|
"dl",
|
||||||
|
"dt",
|
||||||
|
"dd",
|
||||||
|
"br",
|
||||||
|
"details",
|
||||||
|
"summary",
|
||||||
|
]
|
||||||
|
|
||||||
|
ALLOWED_ATTRS = ["src", "width", "height", "href", "class", "open"]
|
||||||
|
|
||||||
|
|
||||||
|
def markdown(value, lazy_load=False):
|
||||||
|
extensions = EXTENSIONS
|
||||||
|
html = _markdown.markdown(
|
||||||
|
value, extensions=extensions, extension_configs=EXTENSION_CONFIGS
|
||||||
|
)
|
||||||
|
|
||||||
|
# Don't clean mathjax
|
||||||
|
hash_script_tag = {}
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
for script_tag in soup.find_all("script"):
|
||||||
|
allow_math_types = ["math/tex", "math/tex; mode=display"]
|
||||||
|
if script_tag.attrs.get("type", False) in allow_math_types:
|
||||||
|
hash_script_tag[str(hash(str(script_tag)))] = str(script_tag)
|
||||||
|
|
||||||
|
for hashed_tag in hash_script_tag:
|
||||||
|
tag = hash_script_tag[hashed_tag]
|
||||||
|
html = html.replace(tag, hashed_tag)
|
||||||
|
|
||||||
|
html = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS)
|
||||||
|
|
||||||
|
for hashed_tag in hash_script_tag:
|
||||||
|
tag = hash_script_tag[hashed_tag]
|
||||||
|
html = html.replace(hashed_tag, tag)
|
||||||
|
|
||||||
|
if not html:
|
||||||
|
html = escape(value)
|
||||||
|
if lazy_load:
|
||||||
|
soup = BeautifulSoup(html, features="html.parser")
|
||||||
|
for img in soup.findAll("img"):
|
||||||
|
if img.get("src"):
|
||||||
|
img["data-src"] = img["src"]
|
||||||
|
img["src"] = ""
|
||||||
|
for img in soup.findAll("iframe"):
|
||||||
|
if img.get("src"):
|
||||||
|
img["data-src"] = img["src"]
|
||||||
|
img["src"] = ""
|
||||||
|
html = str(soup)
|
||||||
|
return '<div class="md-typeset content-description">%s</div>' % html
|
|
@ -24,7 +24,6 @@ def get_resource(request):
|
||||||
scheme = "http"
|
scheme = "http"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"PYGMENT_THEME": settings.PYGMENT_THEME,
|
|
||||||
"INLINE_JQUERY": settings.INLINE_JQUERY,
|
"INLINE_JQUERY": settings.INLINE_JQUERY,
|
||||||
"INLINE_FONTAWESOME": settings.INLINE_FONTAWESOME,
|
"INLINE_FONTAWESOME": settings.INLINE_FONTAWESOME,
|
||||||
"JQUERY_JS": settings.JQUERY_JS,
|
"JQUERY_JS": settings.JQUERY_JS,
|
||||||
|
|
|
@ -406,7 +406,7 @@ class ProblemPdfView(ProblemMixin, SingleObjectMixin, View):
|
||||||
.replace("'//", "'https://")
|
.replace("'//", "'https://")
|
||||||
)
|
)
|
||||||
maker.title = problem_name
|
maker.title = problem_name
|
||||||
assets = ["style.css", "pygment-github.css"]
|
assets = ["style.css"]
|
||||||
if maker.math_engine == "jax":
|
if maker.math_engine == "jax":
|
||||||
assets.append("mathjax3_config.js")
|
assets.append("mathjax3_config.js")
|
||||||
for file in assets:
|
for file in assets:
|
||||||
|
|
|
@ -344,7 +344,7 @@ def problem_init_view(request, problem):
|
||||||
"problem/yaml.html",
|
"problem/yaml.html",
|
||||||
{
|
{
|
||||||
"raw_source": data,
|
"raw_source": data,
|
||||||
"highlighted_source": highlight_code(data, "yaml", linenos=False),
|
"highlighted_source": highlight_code(data, "yaml", linenos=True),
|
||||||
"title": _("Generated init.yml for %s") % problem.name,
|
"title": _("Generated init.yml for %s") % problem.name,
|
||||||
"content_title": mark_safe(
|
"content_title": mark_safe(
|
||||||
escape(_("Generated init.yml for %s"))
|
escape(_("Generated init.yml for %s"))
|
||||||
|
|
|
@ -113,22 +113,6 @@ class SubmissionDetailBase(LoginRequiredMixin, TitleMixin, SubmissionMixin, Deta
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SubmissionSource(SubmissionDetailBase):
|
|
||||||
template_name = "submission/source.html"
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return super().get_queryset().select_related("source")
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(SubmissionSource, self).get_context_data(**kwargs)
|
|
||||||
submission = self.object
|
|
||||||
context["raw_source"] = submission.source.source.rstrip("\n")
|
|
||||||
context["highlighted_source"] = highlight_code(
|
|
||||||
submission.source.source, submission.language.pygments, linenos=False
|
|
||||||
)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
def get_hidden_subtasks(request, submission):
|
def get_hidden_subtasks(request, submission):
|
||||||
contest = submission.contest_object
|
contest = submission.contest_object
|
||||||
if contest and contest.is_editable_by(request.user):
|
if contest and contest.is_editable_by(request.user):
|
||||||
|
@ -227,9 +211,11 @@ class SubmissionStatus(SubmissionDetailBase):
|
||||||
)
|
)
|
||||||
context["time_limit"] = submission.problem.time_limit
|
context["time_limit"] = submission.problem.time_limit
|
||||||
context["can_see_testcases"] = False
|
context["can_see_testcases"] = False
|
||||||
context["raw_source"] = submission.source.source.rstrip("\n")
|
|
||||||
context["highlighted_source"] = highlight_code(
|
context["highlighted_source"] = highlight_code(
|
||||||
submission.source.source, submission.language.pygments, linenos=False
|
submission.source.source,
|
||||||
|
submission.language.pygments,
|
||||||
|
linenos=True,
|
||||||
|
title=submission.language,
|
||||||
)
|
)
|
||||||
|
|
||||||
contest = submission.contest_or_none
|
contest = submission.contest_or_none
|
||||||
|
@ -265,7 +251,7 @@ class SubmissionTestCaseQuery(SubmissionStatus):
|
||||||
return super(SubmissionTestCaseQuery, self).get(request, *args, **kwargs)
|
return super(SubmissionTestCaseQuery, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SubmissionSourceRaw(SubmissionSource):
|
class SubmissionSourceRaw(SubmissionDetailBase):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
submission = self.get_object()
|
submission = self.get_object()
|
||||||
return HttpResponse(submission.source.source, content_type="text/plain")
|
return HttpResponse(submission.source.source, content_type="text/plain")
|
||||||
|
|
|
@ -117,7 +117,6 @@ else:
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
"all": [
|
"all": [
|
||||||
"pygment-github.css",
|
|
||||||
"table.css",
|
"table.css",
|
||||||
"ranks.css",
|
"ranks.css",
|
||||||
"dmmd-preview.css",
|
"dmmd-preview.css",
|
||||||
|
|
|
@ -277,6 +277,34 @@ function registerPopper($trigger, $dropdown) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function populateCopyButton() {
|
||||||
|
var copyButton;
|
||||||
|
$('pre code').each(function () {
|
||||||
|
$(this).before($('<div>', {'class': 'copy-clipboard'})
|
||||||
|
.append(copyButton = $('<span>', {
|
||||||
|
'class': 'btn-clipboard',
|
||||||
|
'data-clipboard-text': $(this).text(),
|
||||||
|
'title': 'Click to copy'
|
||||||
|
}).append('<i class="fa fa-copy"></i><span style="margin-left: 2px">Copy</span>')));
|
||||||
|
|
||||||
|
$(copyButton.get(0)).mouseleave(function () {
|
||||||
|
$(this).attr('class', 'btn-clipboard');
|
||||||
|
$(this).removeAttr('aria-label');
|
||||||
|
});
|
||||||
|
|
||||||
|
var curClipboard = new Clipboard(copyButton.get(0));
|
||||||
|
|
||||||
|
curClipboard.on('success', function (e) {
|
||||||
|
e.clearSelection();
|
||||||
|
showTooltip(e.trigger, 'Copied!');
|
||||||
|
});
|
||||||
|
|
||||||
|
curClipboard.on('error', function (e) {
|
||||||
|
showTooltip(e.trigger, fallbackMessage(e.action));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function onWindowReady() {
|
function onWindowReady() {
|
||||||
// http://stackoverflow.com/a/1060034/1090657
|
// http://stackoverflow.com/a/1060034/1090657
|
||||||
var hidden = 'hidden';
|
var hidden = 'hidden';
|
||||||
|
@ -370,31 +398,8 @@ function onWindowReady() {
|
||||||
})
|
})
|
||||||
$('#logout').on('click', () => $('#logout-form').submit());
|
$('#logout').on('click', () => $('#logout-form').submit());
|
||||||
|
|
||||||
var copyButton;
|
populateCopyButton();
|
||||||
$('pre code').each(function () {
|
|
||||||
$(this).parent().before($('<div>', {'class': 'copy-clipboard'})
|
|
||||||
.append(copyButton = $('<span>', {
|
|
||||||
'class': 'btn-clipboard',
|
|
||||||
'data-clipboard-text': $(this).text(),
|
|
||||||
'title': 'Click to copy'
|
|
||||||
}).text('Copy')));
|
|
||||||
|
|
||||||
$(copyButton.get(0)).mouseleave(function () {
|
|
||||||
$(this).attr('class', 'btn-clipboard');
|
|
||||||
$(this).removeAttr('aria-label');
|
|
||||||
});
|
|
||||||
|
|
||||||
var curClipboard = new Clipboard(copyButton.get(0));
|
|
||||||
|
|
||||||
curClipboard.on('success', function (e) {
|
|
||||||
e.clearSelection();
|
|
||||||
showTooltip(e.trigger, 'Copied!');
|
|
||||||
});
|
|
||||||
|
|
||||||
curClipboard.on('error', function (e) {
|
|
||||||
showTooltip(e.trigger, fallbackMessage(e.action));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$('a').click(function() {
|
$('a').click(function() {
|
||||||
var href = $(this).attr('href');
|
var href = $(this).attr('href');
|
||||||
if (!href || href === '#' || href.startsWith("javascript")) {
|
if (!href || href === '#' || href.startsWith("javascript")) {
|
||||||
|
|
|
@ -21,11 +21,9 @@
|
||||||
font-family: $monospace-fonts !important;
|
font-family: $monospace-fonts !important;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
border: 1px solid $border_gray;
|
background-color: var(--md-code-bg-color);
|
||||||
background-color: #f8f8f8;
|
|
||||||
border-radius: $widget_border_radius;
|
border-radius: $widget_border_radius;
|
||||||
font-size: 0.95em;
|
color: var(--md-code-fg-color);
|
||||||
color: #444;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
@ -36,17 +34,16 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: black;
|
color: var(--md-code-fg-color);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
padding: 1em;
|
padding: 0.5em 1em;
|
||||||
border: 1px solid $border_gray;
|
background-color: var(--md-code-bg-color);
|
||||||
background-color: #f8f8f8;
|
color: var(--md-code-fg-color);
|
||||||
color: black;
|
border-radius: 3px;
|
||||||
border-radius: $widget_border_radius;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre.no-border {
|
pre.no-border {
|
||||||
|
@ -57,6 +54,10 @@
|
||||||
border-radius: none;
|
border-radius: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.linenos pre {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
b, strong {
|
b, strong {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +171,10 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (min-width: 700px) {
|
@media (min-width: 800px) {
|
||||||
|
.content-description pre:has(code) {
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
#common-content {
|
#common-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
@ -206,7 +210,7 @@ pre {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media not all and (min-width: 700px) {
|
@media not all and (min-width: 800px) {
|
||||||
#content-right .info-float {
|
#content-right .info-float {
|
||||||
float: none;
|
float: none;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
|
|
|
@ -4295,7 +4295,7 @@ html .md-typeset .footnote-ref{
|
||||||
padding:0 1.1764705882em
|
padding:0 1.1764705882em
|
||||||
}
|
}
|
||||||
.highlight span.filename{
|
.highlight span.filename{
|
||||||
background-color:var(--md-code-bg-color);
|
background-color:var(--md-default-fg-color--lighter);
|
||||||
border-bottom:.05rem solid var(--md-default-fg-color--lightest);
|
border-bottom:.05rem solid var(--md-default-fg-color--lightest);
|
||||||
border-top-left-radius:.1rem;
|
border-top-left-radius:.1rem;
|
||||||
border-top-right-radius:.1rem;
|
border-top-right-radius:.1rem;
|
||||||
|
@ -4374,8 +4374,6 @@ html .md-typeset .footnote-ref{
|
||||||
background-color:var(--md-code-bg-color);
|
background-color:var(--md-code-bg-color);
|
||||||
border-bottom-left-radius:.1rem;
|
border-bottom-left-radius:.1rem;
|
||||||
border-top-left-radius:.1rem;
|
border-top-left-radius:.1rem;
|
||||||
font-size:.85em;
|
|
||||||
padding:.7720588235em 0 .7720588235em 1.1764705882em;
|
|
||||||
-webkit-user-select:none;
|
-webkit-user-select:none;
|
||||||
-moz-user-select:none;
|
-moz-user-select:none;
|
||||||
-ms-user-select:none;
|
-ms-user-select:none;
|
||||||
|
|
|
@ -155,48 +155,6 @@ label[for="language"], label[for="status"] {
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
.source-ln {
|
|
||||||
color: gray;
|
|
||||||
border-right: 1px solid gray;
|
|
||||||
padding-right: 5px;
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: gray;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
display: block;
|
|
||||||
content: " ";
|
|
||||||
margin-top: -50px;
|
|
||||||
height: 50px;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-code pre, .source-ln pre {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-code {
|
|
||||||
padding-left: 15px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.source-wrap {
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 1em;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: double 4px darkgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistics-table {
|
.statistics-table {
|
||||||
.status {
|
.status {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -200,30 +200,26 @@ input {
|
||||||
|
|
||||||
// Bootstrap-y copy button
|
// Bootstrap-y copy button
|
||||||
.btn-clipboard {
|
.btn-clipboard {
|
||||||
top: 0;
|
top: -5px;
|
||||||
right: 0;
|
right: -8px;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #767676;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #FFF;
|
|
||||||
border: 1px solid #E1E1E8;
|
|
||||||
border-radius: 0 $widget_border_radius;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 5px 8px;
|
padding: 5px 8px;
|
||||||
|
font-family: system-ui, "Noto Sans";
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: $widget_border_radius;
|
||||||
|
border: 1px solid #E1E1E8;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-clipboard {
|
.copy-clipboard {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-typeset .admonition .btn-clipboard,
|
|
||||||
.md-typeset details .btn-clipboard {
|
|
||||||
right: -0.6rem;
|
|
||||||
border-radius: 0 0 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Bootstrap-y tabs
|
// Bootstrap-y tabs
|
||||||
.ul_tab_a_active {
|
.ul_tab_a_active {
|
||||||
color: $theme_color;
|
color: $theme_color;
|
||||||
|
|
|
@ -50,9 +50,7 @@
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static('markdown.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ static('markdown.css') }}">
|
||||||
{% compress css %}
|
{% compress css %}
|
||||||
<link rel="stylesheet" href="{{ static('style.css') }}">
|
<link rel="stylesheet" href="{{ static('style.css') }}">
|
||||||
{% if PYGMENT_THEME %}
|
{% if INLINE_FONTAWESOME %}
|
||||||
<link rel="stylesheet" href="{{ static(PYGMENT_THEME) }}">
|
|
||||||
{% endif %}{% if INLINE_FONTAWESOME %}
|
|
||||||
<link rel="stylesheet" href="{{ static('libs/fontawesome/font-awesome.css') }}">{% endif %}
|
<link rel="stylesheet" href="{{ static('libs/fontawesome/font-awesome.css') }}">{% endif %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/featherlight/featherlight.min.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ static('libs/featherlight/featherlight.min.css') }}">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}">
|
||||||
|
|
|
@ -113,6 +113,7 @@
|
||||||
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
|
||||||
register_time($('.time-with-rel'));
|
register_time($('.time-with-rel'));
|
||||||
MathJax.typeset();
|
MathJax.typeset();
|
||||||
|
populateCopyButton();
|
||||||
merge_authors();
|
merge_authors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +168,7 @@
|
||||||
add_new_message(message, room, true);
|
add_new_message(message, room, true);
|
||||||
}
|
}
|
||||||
MathJax.typeset();
|
MathJax.typeset();
|
||||||
|
populateCopyButton();
|
||||||
register_time($('.time-with-rel'));
|
register_time($('.time-with-rel'));
|
||||||
remove_unread_current_user();
|
remove_unread_current_user();
|
||||||
merge_authors();
|
merge_authors();
|
||||||
|
|
|
@ -21,34 +21,6 @@
|
||||||
info_float.width(container.width());
|
info_float.width(container.width());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this
|
|
||||||
var copyButton;
|
|
||||||
$('pre code').each(function () {
|
|
||||||
$(this).parent().before($('<div>', {'class': 'copy-clipboard'})
|
|
||||||
.append(copyButton = $('<span>', {
|
|
||||||
'class': 'btn-clipboard',
|
|
||||||
'data-clipboard-text': $(this).text(),
|
|
||||||
'title': 'Click to copy'
|
|
||||||
}).text('Copy')));
|
|
||||||
|
|
||||||
$(copyButton.get(0)).mouseleave(function () {
|
|
||||||
$(this).attr('class', 'btn-clipboard');
|
|
||||||
$(this).removeAttr('aria-label');
|
|
||||||
});
|
|
||||||
|
|
||||||
var curClipboard = new Clipboard(copyButton.get(0));
|
|
||||||
|
|
||||||
curClipboard.on('success', function (e) {
|
|
||||||
e.clearSelection();
|
|
||||||
showTooltip(e.trigger, 'Copied!');
|
|
||||||
});
|
|
||||||
|
|
||||||
curClipboard.on('error', function (e) {
|
|
||||||
showTooltip(e.trigger, fallbackMessage(e.action));
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
<hr>
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ tag.description|markdown }}
|
{{ tag.description }}
|
||||||
|
|
|
@ -44,15 +44,13 @@
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "{{url('blog_preview')}}",
|
url: "{{url('blog_preview')}}",
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': csrfToken, // Include the CSRF token in the headers
|
|
||||||
},
|
|
||||||
data: {
|
data: {
|
||||||
preview: $(this).val()
|
preview: $(this).val()
|
||||||
},
|
},
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
$('#display').html(data);
|
$('#display').html(data);
|
||||||
MathJax.typeset();
|
MathJax.typeset();
|
||||||
|
populateCopyButton();
|
||||||
},
|
},
|
||||||
error: function(error) {
|
error: function(error) {
|
||||||
alert(error);
|
alert(error);
|
||||||
|
|
|
@ -1,20 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="source-wrap">
|
{{ highlighted_source }}</td>
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td class="source-ln">
|
|
||||||
<div>
|
|
||||||
{% for line in raw_source.split('\n') %}
|
|
||||||
<a href="#line-{{ loop.index }}" name="line-{{ loop.index }}">
|
|
||||||
<pre>{{ loop.index }}</pre>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="source-code">{{ highlighted_source }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -13,6 +13,6 @@
|
||||||
{% if request.profile.id in submission.problem.editor_ids or perms.judge.edit_all_problem %}
|
{% if request.profile.id in submission.problem.editor_ids or perms.judge.edit_all_problem %}
|
||||||
<hr style="float:left;width:30%"><br>
|
<hr style="float:left;width:30%"><br>
|
||||||
<h4>{{ _('Error information') }}</h4>
|
<h4>{{ _('Error information') }}</h4>
|
||||||
<code>{{ submission.error|highlight('pytb', linenos=False) }}</code>
|
<code>{{ submission.error|highlight('pytb', linenos=True) }}</code>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,64 +0,0 @@
|
||||||
{% extends "submission/info-base.html" %}
|
|
||||||
{% block media %}
|
|
||||||
<style>
|
|
||||||
.line {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlighter {
|
|
||||||
position: absolute;
|
|
||||||
width: 9999px;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:active .line .highlighter {
|
|
||||||
background: rgba(255, 212, 0, 0.48);
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-clipboard {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div style="clear: both"></div>
|
|
||||||
<br>
|
|
||||||
<div><a href="{{ url('submission_status', submission.id) }}">{{ _('View status') }}</a></div>
|
|
||||||
<div><a href="{{ url('submission_source_raw', submission.id) }}">{{ _('View raw source') }}</a></div>
|
|
||||||
{% if request.user == submission.user.user or perms.judge.resubmit_other %}
|
|
||||||
<div><a href="{{ url('problem_submit', submission.problem.code, submission.id) }}">{{ _('Resubmit') }}</a></div>
|
|
||||||
{% endif %}
|
|
||||||
{% if perms.judge.rejudge_submission %}
|
|
||||||
<div>
|
|
||||||
<form action="{{ url('submission_rejudge') }}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<a href="#" onclick="parentNode.submit()">{{ _('Rejudge') }}</a>
|
|
||||||
<input type="hidden" name="id" value="{{ submission.id }}">
|
|
||||||
<input type="hidden" name="path" value="{{ url('submission_status', submission.id) }}">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<br>
|
|
||||||
<hr>
|
|
||||||
<br>
|
|
||||||
<div class="source-wrap">
|
|
||||||
<table style="width: 100%">
|
|
||||||
<tr>
|
|
||||||
<td class="source-ln" style="width: 0">
|
|
||||||
<div>
|
|
||||||
{% for line in raw_source.split('\n') %}
|
|
||||||
<a href="#line-{{ loop.index }}" name="line-{{ loop.index }}">
|
|
||||||
<pre class="line">{{ loop.index }}</pre>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="source-code">{{ highlighted_source }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -135,7 +135,7 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div style="clear: both"></div>
|
<div style="clear: both"></div>
|
||||||
<br>
|
<br>
|
||||||
{% if request.user == submission.user.user or perms.judge.resubmit_other %}
|
{% if request.profile == submission.user or perms.judge.resubmit_other %}
|
||||||
<div><a href="{{ url('problem_submit', submission.problem.code, submission.id) }}">{{ _('Resubmit') }}</a></div>
|
<div><a href="{{ url('problem_submit', submission.problem.code, submission.id) }}">{{ _('Resubmit') }}</a></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.judge.rejudge_submission %}
|
{% if perms.judge.rejudge_submission %}
|
||||||
|
@ -151,21 +151,8 @@
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h3 id="source-header" class="toggle closed"><i class="fa fa-chevron-right fa-fw"></i>{{_('Source code')}}</h3>
|
<h3 id="source-header" class="toggle closed"><i class="fa fa-chevron-right fa-fw"></i>{{_('Source code')}}</h3>
|
||||||
<div class="source-wrap toggled" style="display: none; margin-bottom: 1em">
|
<div class="toggled" style="display: none; margin-bottom: 1em">
|
||||||
<table style="width: 100%">
|
{{ highlighted_source }}
|
||||||
<tr>
|
|
||||||
<td class="source-ln" style="width: 0">
|
|
||||||
<div>
|
|
||||||
{% for line in raw_source.split('\n') %}
|
|
||||||
<a href="#line-{{ loop.index }}" name="line-{{ loop.index }}">
|
|
||||||
<pre class="line">{{ loop.index }}</pre>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="source-code">{{ highlighted_source }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="test-cases">{% include "submission/status-testcases.html" %}</div>
|
<div id="test-cases">{% include "submission/status-testcases.html" %}</div>
|
||||||
|
|
Loading…
Reference in a new issue