mirror of
https://github.com/DMOJ/online-judge.git
synced 2024-11-25 16:32:37 +08:00
Revert "Completely remove Pagedown" and "Switch all Pagedown editors to Martor"
This reverts commits: -2035e606f3
-ce1196f74f
Since they do not interact well with dark mode.
This commit is contained in:
parent
6baf5c3149
commit
c126f07fe7
3
.flake8
3
.flake8
@ -18,6 +18,9 @@ per-file-ignores =
|
|||||||
./judge/management/commands/runmoss.py:F403,F405
|
./judge/management/commands/runmoss.py:F403,F405
|
||||||
# E501: line too long, ignore in migrations
|
# E501: line too long, ignore in migrations
|
||||||
./judge/migrations/*.py:E501
|
./judge/migrations/*.py:E501
|
||||||
|
# E303: too many blank lines
|
||||||
|
# PyCharm likes to have double lines between class/def in an if statement.
|
||||||
|
./judge/widgets/pagedown.py:E303
|
||||||
exclude =
|
exclude =
|
||||||
# belongs to the user
|
# belongs to the user
|
||||||
./dmoj/local_settings.py,
|
./dmoj/local_settings.py,
|
||||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,3 +1,7 @@
|
|||||||
|
[submodule "resources/pagedown"]
|
||||||
|
path = resources/pagedown
|
||||||
|
url = https://github.com/DMOJ/dmoj-pagedown.git
|
||||||
|
branch = master
|
||||||
[submodule "resources/libs"]
|
[submodule "resources/libs"]
|
||||||
path = resources/libs
|
path = resources/libs
|
||||||
url = https://github.com/DMOJ/site-assets.git
|
url = https://github.com/DMOJ/site-assets.git
|
||||||
|
@ -269,6 +269,7 @@ INSTALLED_APPS += (
|
|||||||
'social_django',
|
'social_django',
|
||||||
'compressor',
|
'compressor',
|
||||||
'django_ace',
|
'django_ace',
|
||||||
|
'pagedown',
|
||||||
'sortedm2m',
|
'sortedm2m',
|
||||||
'statici18n',
|
'statici18n',
|
||||||
'impersonate',
|
'impersonate',
|
||||||
|
@ -20,7 +20,7 @@ from reversion.models import Revision, Version
|
|||||||
|
|
||||||
from judge.dblock import LockModel
|
from judge.dblock import LockModel
|
||||||
from judge.models import Comment, CommentLock
|
from judge.models import Comment, CommentLock
|
||||||
from judge.widgets import MartorWidget
|
from judge.widgets import HeavyPreviewPageDownWidget
|
||||||
|
|
||||||
|
|
||||||
class CommentForm(ModelForm):
|
class CommentForm(ModelForm):
|
||||||
@ -31,7 +31,9 @@ class CommentForm(ModelForm):
|
|||||||
'parent': forms.HiddenInput(),
|
'parent': forms.HiddenInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
widgets['body'] = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('comment_preview')})
|
if HeavyPreviewPageDownWidget is not None:
|
||||||
|
widgets['body'] = HeavyPreviewPageDownWidget(preview=reverse_lazy('comment_preview'),
|
||||||
|
preview_timeout=1000, hide_preview_button=True)
|
||||||
|
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
@ -20,7 +20,7 @@ from judge.models import Contest, Language, Organization, Problem, ProblemPoints
|
|||||||
WebAuthnCredential
|
WebAuthnCredential
|
||||||
from judge.utils.mail import validate_email_domain
|
from judge.utils.mail import validate_email_domain
|
||||||
from judge.utils.subscription import newsletter_id
|
from judge.utils.subscription import newsletter_id
|
||||||
from judge.widgets import MartorWidget, Select2MultipleWidget, Select2Widget
|
from judge.widgets import HeavyPreviewPageDownWidget, Select2MultipleWidget, Select2Widget
|
||||||
|
|
||||||
TOTP_CODE_LENGTH = 6
|
TOTP_CODE_LENGTH = 6
|
||||||
|
|
||||||
@ -63,7 +63,11 @@ class ProfileForm(ModelForm):
|
|||||||
fields.append('math_engine')
|
fields.append('math_engine')
|
||||||
widgets['math_engine'] = Select2Widget(attrs={'style': 'width:200px'})
|
widgets['math_engine'] = Select2Widget(attrs={'style': 'width:200px'})
|
||||||
|
|
||||||
widgets['about'] = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('profile_preview')})
|
if HeavyPreviewPageDownWidget is not None:
|
||||||
|
widgets['about'] = HeavyPreviewPageDownWidget(
|
||||||
|
preview=reverse_lazy('profile_preview'),
|
||||||
|
attrs={'style': 'max-width:700px;min-width:700px;width:700px'},
|
||||||
|
)
|
||||||
|
|
||||||
def clean_about(self):
|
def clean_about(self):
|
||||||
if 'about' in self.changed_data and not self.instance.has_any_solves:
|
if 'about' in self.changed_data and not self.instance.has_any_solves:
|
||||||
@ -169,7 +173,8 @@ class EditOrganizationForm(ModelForm):
|
|||||||
model = Organization
|
model = Organization
|
||||||
fields = ['about', 'logo_override_image', 'admins']
|
fields = ['about', 'logo_override_image', 'admins']
|
||||||
widgets = {'admins': Select2MultipleWidget(attrs={'style': 'width: 200px'})}
|
widgets = {'admins': Select2MultipleWidget(attrs={'style': 'width: 200px'})}
|
||||||
widgets['about'] = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('organization_preview')})
|
if HeavyPreviewPageDownWidget is not None:
|
||||||
|
widgets['about'] = HeavyPreviewPageDownWidget(preview=reverse_lazy('organization_preview'))
|
||||||
|
|
||||||
|
|
||||||
class CustomAuthenticationForm(AuthenticationForm):
|
class CustomAuthenticationForm(AuthenticationForm):
|
||||||
|
@ -7,7 +7,6 @@ from django.forms.models import ModelForm
|
|||||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, \
|
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, \
|
||||||
HttpResponseRedirect
|
HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
from django.views.generic import DetailView, UpdateView
|
from django.views.generic import DetailView, UpdateView
|
||||||
@ -17,7 +16,7 @@ from reversion.models import Version
|
|||||||
from judge.dblock import LockModel
|
from judge.dblock import LockModel
|
||||||
from judge.models import Comment, CommentVote
|
from judge.models import Comment, CommentVote
|
||||||
from judge.utils.views import TitleMixin
|
from judge.utils.views import TitleMixin
|
||||||
from judge.widgets import MartorWidget
|
from judge.widgets import MathJaxPagedownWidget
|
||||||
|
|
||||||
__all__ = ['upvote_comment', 'downvote_comment', 'CommentEditAjax', 'CommentContent',
|
__all__ = ['upvote_comment', 'downvote_comment', 'CommentEditAjax', 'CommentContent',
|
||||||
'CommentEdit']
|
'CommentEdit']
|
||||||
@ -123,14 +122,8 @@ class CommentEditForm(ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Comment
|
model = Comment
|
||||||
fields = ['body']
|
fields = ['body']
|
||||||
widgets = {
|
if MathJaxPagedownWidget is not None:
|
||||||
'body': MartorWidget(
|
widgets = {'body': MathJaxPagedownWidget(attrs={'id': 'id-edit-comment-body'})}
|
||||||
attrs={
|
|
||||||
'id': 'id_edit',
|
|
||||||
'data-markdownfy-url': reverse_lazy('comment_preview'),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView):
|
class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView):
|
||||||
|
@ -22,9 +22,11 @@ from judge.utils.diggpaginator import DiggPaginator
|
|||||||
from judge.utils.tickets import filter_visible_tickets, own_ticket_filter
|
from judge.utils.tickets import filter_visible_tickets, own_ticket_filter
|
||||||
from judge.utils.views import SingleObjectFormView, TitleMixin, paginate_query_context
|
from judge.utils.views import SingleObjectFormView, TitleMixin, paginate_query_context
|
||||||
from judge.views.problem import ProblemMixin
|
from judge.views.problem import ProblemMixin
|
||||||
from judge.widgets import MartorWidget
|
from judge.widgets import HeavyPreviewPageDownWidget
|
||||||
|
|
||||||
ticket_widget = MartorWidget(attrs={'data-markdownfy-url': reverse_lazy('ticket_preview')})
|
ticket_widget = (forms.Textarea() if HeavyPreviewPageDownWidget is None else
|
||||||
|
HeavyPreviewPageDownWidget(preview=reverse_lazy('ticket_preview'),
|
||||||
|
preview_timeout=1000, hide_preview_button=True))
|
||||||
|
|
||||||
|
|
||||||
class TicketForm(forms.Form):
|
class TicketForm(forms.Form):
|
||||||
|
@ -6,9 +6,9 @@ from urllib.parse import urljoin
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound, \
|
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect
|
||||||
HttpResponseRedirect
|
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
from martor.api import imgur_uploader
|
||||||
|
|
||||||
from judge.models import Submission
|
from judge.models import Submission
|
||||||
|
|
||||||
@ -51,11 +51,12 @@ def django_uploader(image):
|
|||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def martor_image_uploader(request):
|
def martor_image_uploader(request):
|
||||||
if not request.user.is_staff:
|
|
||||||
return HttpResponseNotFound()
|
|
||||||
if request.method != 'POST' or not request.is_ajax() or 'markdown-image-upload' not in request.FILES:
|
if request.method != 'POST' or not request.is_ajax() or 'markdown-image-upload' not in request.FILES:
|
||||||
return HttpResponseBadRequest('Invalid request')
|
return HttpResponseBadRequest('Invalid request')
|
||||||
|
|
||||||
image = request.FILES['markdown-image-upload']
|
image = request.FILES['markdown-image-upload']
|
||||||
data = django_uploader(image)
|
if request.user.is_staff:
|
||||||
|
data = django_uploader(image)
|
||||||
|
else:
|
||||||
|
data = imgur_uploader(image)
|
||||||
return HttpResponse(data, content_type='application/json')
|
return HttpResponse(data, content_type='application/json')
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from judge.widgets.checkbox import CheckboxSelectMultipleWithSelectAll
|
from judge.widgets.checkbox import CheckboxSelectMultipleWithSelectAll
|
||||||
from judge.widgets.martor import *
|
from judge.widgets.martor import *
|
||||||
from judge.widgets.mixins import CompressorWidgetMixin
|
from judge.widgets.mixins import CompressorWidgetMixin
|
||||||
|
from judge.widgets.pagedown import *
|
||||||
from judge.widgets.select2 import *
|
from judge.widgets.select2 import *
|
||||||
|
@ -12,8 +12,6 @@ class MartorWidget(OldMartorWidget):
|
|||||||
|
|
||||||
|
|
||||||
class AdminMartorWidget(OldAdminMartorWidget):
|
class AdminMartorWidget(OldAdminMartorWidget):
|
||||||
UPLOADS_ENABLED = True
|
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = MartorWidget.Media.css
|
css = MartorWidget.Media.css
|
||||||
js = ['admin/js/jquery.init.js', 'martor-mathjax.js']
|
js = ['admin/js/jquery.init.js', 'martor-mathjax.js']
|
||||||
|
67
judge/widgets/pagedown.py
Normal file
67
judge/widgets/pagedown.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from django.forms.utils import flatatt
|
||||||
|
from django.template.loader import get_template
|
||||||
|
from django.utils.encoding import force_str
|
||||||
|
from django.utils.html import conditional_escape
|
||||||
|
|
||||||
|
from judge.widgets.mixins import CompressorWidgetMixin
|
||||||
|
|
||||||
|
__all__ = ['PagedownWidget', 'MathJaxPagedownWidget', 'HeavyPreviewPageDownWidget']
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pagedown.widgets import PagedownWidget as OldPagedownWidget
|
||||||
|
except ImportError:
|
||||||
|
PagedownWidget = None
|
||||||
|
MathJaxPagedownWidget = None
|
||||||
|
HeavyPreviewPageDownWidget = None
|
||||||
|
else:
|
||||||
|
class PagedownWidget(CompressorWidgetMixin, OldPagedownWidget):
|
||||||
|
# The goal here is to compress all the pagedown JS into one file.
|
||||||
|
# We do not want any further compress down the chain, because
|
||||||
|
# 1. we'll create multiple large JS files to download.
|
||||||
|
# 2. this is not a problem here because all the pagedown JS files will be used together.
|
||||||
|
compress_js = True
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs.setdefault('css', ())
|
||||||
|
super(PagedownWidget, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MathJaxPagedownWidget(PagedownWidget):
|
||||||
|
class Media:
|
||||||
|
js = [
|
||||||
|
'mathjax_config.js',
|
||||||
|
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.0/es5/tex-chtml.min.js',
|
||||||
|
'pagedown_math.js',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class HeavyPreviewPageDownWidget(PagedownWidget):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs.setdefault('template', 'pagedown.html')
|
||||||
|
self.preview_url = kwargs.pop('preview')
|
||||||
|
self.preview_timeout = kwargs.pop('preview_timeout', None)
|
||||||
|
self.hide_preview_button = kwargs.pop('hide_preview_button', False)
|
||||||
|
super(HeavyPreviewPageDownWidget, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
|
if value is None:
|
||||||
|
value = ''
|
||||||
|
final_attrs = self.build_attrs(attrs, {'name': name})
|
||||||
|
if 'class' not in final_attrs:
|
||||||
|
final_attrs['class'] = ''
|
||||||
|
final_attrs['class'] += ' wmd-input'
|
||||||
|
return get_template(self.template).render(self.get_template_context(final_attrs, value))
|
||||||
|
|
||||||
|
def get_template_context(self, attrs, value):
|
||||||
|
return {
|
||||||
|
'attrs': flatatt(attrs),
|
||||||
|
'body': conditional_escape(force_str(value)),
|
||||||
|
'id': attrs['id'],
|
||||||
|
'show_preview': self.show_preview,
|
||||||
|
'preview_url': self.preview_url,
|
||||||
|
'preview_timeout': self.preview_timeout,
|
||||||
|
'extra_classes': 'dmmd-no-button' if self.hide_preview_button else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Media:
|
||||||
|
js = ['dmmd-preview.js']
|
@ -1,6 +1,7 @@
|
|||||||
Django>=3.2,<4
|
Django>=3.2,<4
|
||||||
django_compressor>=3
|
django_compressor>=3
|
||||||
django-mptt>=0.13
|
django-mptt>=0.13
|
||||||
|
django-pagedown<2
|
||||||
django-registration-redux>=2.10
|
django-registration-redux>=2.10
|
||||||
django-reversion>=3.0.5,<4
|
django-reversion>=3.0.5,<4
|
||||||
django-social-share
|
django-social-share
|
||||||
|
10
resources/admin/css/pagedown.css
Normal file
10
resources/admin/css/pagedown.css
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.wmd-wrapper {
|
||||||
|
padding-right: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.wmd-preview {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
@ -60,10 +60,6 @@ a {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-edit-form {
|
|
||||||
min-width: 60em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-post-wrapper {
|
.comment-post-wrapper {
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
|
|
||||||
|
99
resources/dmmd-preview.js
Normal file
99
resources/dmmd-preview.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
$(function () {
|
||||||
|
window.register_dmmd_preview = function ($preview) {
|
||||||
|
var $form = $preview.parents('form').first();
|
||||||
|
var $update = $preview.find('.dmmd-preview-update');
|
||||||
|
var $content = $preview.find('.dmmd-preview-content');
|
||||||
|
var preview_url = $preview.attr('data-preview-url');
|
||||||
|
var $textarea = $('#' + $preview.attr('data-textarea-id'));
|
||||||
|
|
||||||
|
// Submit the form if Ctrl+Enter is pressed in pagedown textarea.
|
||||||
|
$textarea.keydown(function (ev) {
|
||||||
|
// Ctrl+Enter pressed (metaKey used to support command key on mac).
|
||||||
|
if ((ev.metaKey || ev.ctrlKey) && ev.which == 13) {
|
||||||
|
$form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$update.click(function () {
|
||||||
|
var text = $textarea.val();
|
||||||
|
if (text) {
|
||||||
|
$preview.addClass('dmmd-preview-stale');
|
||||||
|
$.post(preview_url, {
|
||||||
|
content: text,
|
||||||
|
csrfmiddlewaretoken: $.cookie('csrftoken')
|
||||||
|
}, function (result) {
|
||||||
|
$content.html(result);
|
||||||
|
$preview.addClass('dmmd-preview-has-content').removeClass('dmmd-preview-stale');
|
||||||
|
|
||||||
|
var $jax = $content.find('.require-mathjax-support');
|
||||||
|
if ($jax.length) {
|
||||||
|
if (!('MathJax' in window)) {
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: $jax.attr('data-config'),
|
||||||
|
dataType: 'script',
|
||||||
|
cache: true,
|
||||||
|
success: function () {
|
||||||
|
$.ajax({
|
||||||
|
type: 'GET',
|
||||||
|
url: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.0/es5/tex-chtml.min.js',
|
||||||
|
dataType: 'script',
|
||||||
|
cache: true,
|
||||||
|
success: function () {
|
||||||
|
MathJax.typesetPromise([$content[0]]).then(function () {
|
||||||
|
$content.find('.tex-image').hide();
|
||||||
|
$content.find('.tex-text').show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
MathJax.typesetPromise([$content[0]]).then(function () {
|
||||||
|
$content.find('.tex-image').hide();
|
||||||
|
$content.find('.tex-text').show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$content.empty();
|
||||||
|
$preview.removeClass('dmmd-preview-has-content').removeClass('dmmd-preview-stale');
|
||||||
|
}
|
||||||
|
}).click();
|
||||||
|
|
||||||
|
var timeout = $preview.attr('data-timeout');
|
||||||
|
var last_event = null;
|
||||||
|
var last_text = $textarea.val();
|
||||||
|
if (timeout) {
|
||||||
|
$textarea.on('keyup paste', function () {
|
||||||
|
var text = $textarea.val();
|
||||||
|
if (last_text == text) return;
|
||||||
|
last_text = text;
|
||||||
|
|
||||||
|
$preview.addClass('dmmd-preview-stale');
|
||||||
|
if (last_event)
|
||||||
|
clearTimeout(last_event);
|
||||||
|
last_event = setTimeout(function () {
|
||||||
|
$update.click();
|
||||||
|
last_event = null;
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$('.dmmd-preview').each(function () {
|
||||||
|
register_dmmd_preview($(this));
|
||||||
|
});
|
||||||
|
|
||||||
|
if ('django' in window && 'jQuery' in window.django)
|
||||||
|
django.jQuery(document).on('formset:added', function(event, $row) {
|
||||||
|
var $preview = $row.find('.dmmd-preview');
|
||||||
|
if ($preview.length) {
|
||||||
|
var id = $row.attr('id');
|
||||||
|
id = id.substr(id.lastIndexOf('-') + 1);
|
||||||
|
$preview.attr('data-textarea-id', $preview.attr('data-textarea-id').replace('__prefix__', id));
|
||||||
|
register_dmmd_preview($preview);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
43
resources/dmmd-preview.scss
Normal file
43
resources/dmmd-preview.scss
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
@import "vars";
|
||||||
|
|
||||||
|
div.dmmd-preview {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-preview-update {
|
||||||
|
background: $color_primary25;
|
||||||
|
color: $color_primary75;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-preview-content {
|
||||||
|
padding: 0 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-preview.dmmd-preview-has-content div.dmmd-preview-update {
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-preview-has-content div.dmmd-preview-content {
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-no-button div.dmmd-preview-update {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-no-button div.dmmd-preview-content {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-no-button:not(.dmmd-preview-has-content) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.dmmd-preview-stale {
|
||||||
|
background: repeating-linear-gradient(-45deg, $color_primary0, $color_primary0 10px, $color_primary5 10px, $color_primary5 20px);
|
||||||
|
}
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
form .martor-preview {
|
form .martor-preview {
|
||||||
@include content-description;
|
@include content-description;
|
||||||
|
|
||||||
min-height: 400px;
|
|
||||||
|
|
||||||
ul li {
|
ul li {
|
||||||
list-style: unset !important;
|
list-style: unset !important;
|
||||||
@ -23,7 +21,7 @@ form .martor-preview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.section-martor {
|
.section-martor {
|
||||||
div[data-tab^="editor-tab-"] {
|
div[data-tab="editor-tab-description"] {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,12 +33,6 @@ form .martor-preview {
|
|||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.martor-toolbar {
|
|
||||||
flex: 0 1 auto !important;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.martor-field {
|
.martor-field {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.centered-form {
|
.centered-form {
|
||||||
max-width: 800px;
|
max-width: 700px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
.submit-bar {
|
.submit-bar {
|
||||||
|
1
resources/pagedown
Submodule
1
resources/pagedown
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 6e5eac43883a314d1e0dbc5e70069798859eacbc
|
125
resources/pagedown-widget.scss
Normal file
125
resources/pagedown-widget.scss
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
@import "vars";
|
||||||
|
|
||||||
|
.wmd-panel {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-button-bar {
|
||||||
|
width: 100%;
|
||||||
|
background-color: Silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-input {
|
||||||
|
height: 300px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
background: $color_primary0;
|
||||||
|
border: 1px solid $color_primary50;
|
||||||
|
font-family: Consolas, "Liberation Mono", Monaco, "Courier New", monospace !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview {
|
||||||
|
background: none;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-button-row {
|
||||||
|
margin: 5px;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-spacer {
|
||||||
|
width: 15px;
|
||||||
|
height: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-button {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-button > span {
|
||||||
|
@include vars-img;
|
||||||
|
background: url($path_to_root + '/pagedown/wmd-buttons.png') no-repeat 0 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-prompt-background {
|
||||||
|
background-color: Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-prompt-dialog {
|
||||||
|
border: 1px solid $color_primary25;
|
||||||
|
background-color: $color_primary5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-prompt-dialog > div {
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-family: arial, helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-prompt-dialog > form > input[type="text"] {
|
||||||
|
border: 1px solid $color_primary25;
|
||||||
|
color: $color_primary100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-prompt-dialog > form > input[type="button"] {
|
||||||
|
border: 1px solid $color_primary50;
|
||||||
|
font-family: trebuchet MS, helvetica, sans-serif;
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-wrapper {
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 7px;
|
||||||
|
background: $color_primary0;
|
||||||
|
line-height: 1.5em;
|
||||||
|
font-size: 1em;
|
||||||
|
border: 1px solid $color_primary50;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview h1, .wmd-preview h2, .wmd-preview h3, .wmd-preview h4, .wmd-preview h5, .wmd-preview h6 {
|
||||||
|
font-weight: bold !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview:not(.dmmd-preview) h1 {
|
||||||
|
font-size: 1.6em !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview:not(.dmmd-preview) h2 {
|
||||||
|
font-size: 1.4em !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview:not(.dmmd-preview) h3 {
|
||||||
|
font-size: 1em !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-preview:not(.dmmd-preview) h4, .wmd-preview:not(.dmmd-preview) h5, .wmd-preview:not(.dmmd-preview) h6 {
|
||||||
|
font-size: .9em !important
|
||||||
|
}
|
19
resources/pagedown_math.js
Normal file
19
resources/pagedown_math.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
function mathjax_pagedown($) {
|
||||||
|
if ('MathJax' in window) {
|
||||||
|
$.each(window.editors, function (id, editor) {
|
||||||
|
var preview = $('div.wmd-preview#' + id + '_wmd_preview')[0];
|
||||||
|
if (preview) {
|
||||||
|
editor.hooks.chain('onPreviewRefresh', function () {
|
||||||
|
MathJax.typeset([preview]);
|
||||||
|
});
|
||||||
|
MathJax.typeset([preview]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.mathjax_pagedown = mathjax_pagedown;
|
||||||
|
|
||||||
|
$(window).on('load', function () {
|
||||||
|
(mathjax_pagedown)('$' in window ? $ : django.jQuery);
|
||||||
|
});
|
@ -14,6 +14,8 @@
|
|||||||
@import "widgets";
|
@import "widgets";
|
||||||
@import "featherlight";
|
@import "featherlight";
|
||||||
@import "comments";
|
@import "comments";
|
||||||
|
@import "pagedown-widget";
|
||||||
|
@import "dmmd-preview";
|
||||||
@import "submission";
|
@import "submission";
|
||||||
@import "contest";
|
@import "contest";
|
||||||
@import "misc";
|
@import "misc";
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
form#ticket-form {
|
form#ticket-form {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 800px;
|
max-width: 750px;
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
|
|
||||||
#id_title {
|
#id_title {
|
||||||
@ -72,7 +72,7 @@ div.ticket-title {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap-reverse;
|
flex-wrap: wrap-reverse;
|
||||||
max-width: 1200px;
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticket-sidebar {
|
.ticket-sidebar {
|
||||||
|
@ -6,6 +6,18 @@
|
|||||||
line-height: unset !important;
|
line-height: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wmd-wrapper {
|
||||||
|
padding-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-wrapper ul.wmd-button-row {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmd-input {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
#content .content-description h1,
|
#content .content-description h1,
|
||||||
#content .content-description h2,
|
#content .content-description h2,
|
||||||
#content .content-description h3,
|
#content .content-description h3,
|
||||||
@ -53,3 +65,8 @@ select#id_tags.django-select2 {
|
|||||||
max-width: unset;
|
max-width: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dmmd-preview-update {
|
||||||
|
position: sticky;
|
||||||
|
top: 38px;
|
||||||
|
}
|
||||||
|
@ -48,4 +48,5 @@
|
|||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
{% include "mathjax-load.html" %}
|
{% include "mathjax-load.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% include "comments/math.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="comment-submit comment-edit-form">
|
<div class="comment-submit">
|
||||||
<form id="comment-edit" action="{{ request.get_full_path() }}" method="post">
|
<form id="comment-edit" action="{{ request.get_full_path() }}" method="post">
|
||||||
<span style="display: none" class="comment-id">{{ comment.id }}</span>
|
<span style="display: none" class="comment-id">{{ comment.id }}</span>
|
||||||
<span style="display: none" class="read-back">{{ url('comment_content', comment.id) }}</span>
|
<span style="display: none" class="read-back">{{ url('comment_content', comment.id) }}</span>
|
||||||
|
3
templates/comments/math.html
Normal file
3
templates/comments/math.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{% compress js, inline %}
|
||||||
|
<script src="{{ static('pagedown_math.js') }}"></script>
|
||||||
|
{% endcompress %}
|
@ -2,40 +2,25 @@
|
|||||||
{% compress js %}
|
{% compress js %}
|
||||||
{{ comment_form.media.js }}
|
{{ comment_form.media.js }}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function rename_martor_widget(widget, old_name, new_name) {
|
|
||||||
function rename_data(data_name, prefix) {
|
|
||||||
widget.find('[data-' + data_name + '=' + prefix + old_name + ']')
|
|
||||||
.data(data_name, prefix + new_name)
|
|
||||||
.attr('data-' + data_name, prefix + new_name);
|
|
||||||
}
|
|
||||||
function rename_id(prefix) {
|
|
||||||
widget.find('#' + prefix + old_name).prop('id', prefix + new_name);
|
|
||||||
}
|
|
||||||
function rename_class(prefix) {
|
|
||||||
widget.find('.' + prefix + old_name).removeClass(prefix + old_name).addClass(prefix + new_name);
|
|
||||||
}
|
|
||||||
rename_data('tab', 'editor-tab-');
|
|
||||||
rename_data('tab', 'preview-tab-');
|
|
||||||
rename_data('field-name', '');
|
|
||||||
rename_id('id_');
|
|
||||||
rename_id('martor-');
|
|
||||||
rename_class('martor-field-');
|
|
||||||
rename_class('main-martor-');
|
|
||||||
widget.find('.main-martor').martor();
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
window.reply_comment = function (parent) {
|
window.reply_comment = function (parent) {
|
||||||
var $comment_reply = $('#comment-' + parent + '-reply');
|
var $comment_reply = $('#comment-' + parent + '-reply');
|
||||||
var reply_id = 'reply-' + parent;
|
var reply_id = 'reply-' + parent;
|
||||||
|
var new_id = 'id' + parent + '_body';
|
||||||
if ($comment_reply.find('#' + reply_id).length == 0) {
|
if ($comment_reply.find('#' + reply_id).length == 0) {
|
||||||
var $reply_form = $('#new-comment').clone(false).prop('id', reply_id);
|
var $reply_form = $('#new-comment').clone(true).prop('id', reply_id);
|
||||||
$reply_form.find('h3').html('{{ _('Replying to comment') }}');
|
$reply_form.find('h3').html('{{ _('Replying to comment') }}');
|
||||||
$reply_form.prepend('<a class="close">x</a>');
|
$reply_form.prepend('<a class="close">x</a>');
|
||||||
$reply_form.appendTo($comment_reply);
|
|
||||||
$reply_form.find('form.comment-submit-form input#id_parent').val(parent);
|
$reply_form.find('form.comment-submit-form input#id_parent').val(parent);
|
||||||
rename_martor_widget($reply_form, 'body', reply_id);
|
$reply_form.find('div#id_body-wmd-wrapper').prop('id', new_id + '-wmd-wrapper');
|
||||||
ace.edit('martor-' + reply_id).setValue('');
|
$reply_form.find('div#id_body_wmd_button_bar').empty().prop('id', new_id + '_wmd_button_bar');
|
||||||
|
$reply_form.find('textarea.wmd-input').val('').prop('id', new_id);
|
||||||
|
$reply_form.find('div#id_body-preview').attr('data-textarea-id', new_id).prop('id', new_id + '-preview');
|
||||||
|
$reply_form.appendTo($comment_reply);
|
||||||
|
register_dmmd_preview($reply_form.find('div#' + new_id + '-preview'));
|
||||||
|
if ('DjangoPagedown' in window) {
|
||||||
|
window.DjangoPagedown.createEditor($reply_form.find('textarea.wmd-input').get(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$comment_reply.fadeIn();
|
$comment_reply.fadeIn();
|
||||||
|
|
||||||
@ -163,8 +148,19 @@
|
|||||||
|
|
||||||
$comments.find('a.edit-link').featherlight({
|
$comments.find('a.edit-link').featherlight({
|
||||||
afterOpen: function () {
|
afterOpen: function () {
|
||||||
var $widget = $('.featherlight #comment-form-body');
|
if ('DjangoPagedown' in window) {
|
||||||
rename_martor_widget($widget, 'body', 'edit');
|
var $wmd = $('.featherlight .wmd-input');
|
||||||
|
if ($wmd.length) {
|
||||||
|
window.DjangoPagedown.createEditor($wmd.get(0));
|
||||||
|
if ('MathJax' in window) {
|
||||||
|
var preview = $('.featherlight div.wmd-preview')[0];
|
||||||
|
window.editors[$wmd.attr('id')].hooks.chain('onPreviewRefresh', function () {
|
||||||
|
MathJax.typesetPromise([preview]);
|
||||||
|
});
|
||||||
|
MathJax.typesetPromise([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();
|
||||||
@ -190,6 +186,14 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
beforeClose: function () {
|
||||||
|
if ('DjangoPagedown' in window) {
|
||||||
|
var $wmd = $('.featherlight .wmd-input');
|
||||||
|
if ($wmd.length) {
|
||||||
|
window.DjangoPagedown.destroyEditor($wmd.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
variant: 'featherlight-edit'
|
variant: 'featherlight-edit'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -244,3 +244,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block description_end %}{% endblock %}
|
{% block description_end %}{% endblock %}
|
||||||
|
|
||||||
|
{% block bodyend %}
|
||||||
|
{{ super() }}
|
||||||
|
{% include "comments/math.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
14
templates/pagedown.html
Normal file
14
templates/pagedown.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<div id="{{ id|safe }}-wmd-wrapper" class="wmd-wrapper">
|
||||||
|
<div class="wmd-panel">
|
||||||
|
<div id="{{ id|safe }}_wmd_button_bar"></div>
|
||||||
|
<textarea{{ attrs|safe }}>{{ body }}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if show_preview %}
|
||||||
|
<div id="{{ id|safe }}-preview" data-preview-url="{{ preview_url }}" data-textarea-id="{{ id }}"
|
||||||
|
data-timeout="{{ preview_timeout or '' }}" class="wmd-panel wmd-preview dmmd-preview {{ extra_classes }}">
|
||||||
|
<div class="dmmd-preview-update"><i class="fa fa-refresh"></i> {{ _('Update preview') }}</div>
|
||||||
|
<div class="dmmd-preview-content content-description"></div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
9
templates/pagedown/widgets/default.html
Normal file
9
templates/pagedown/widgets/default.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<div class="wmd-wrapper" id="{{ id }}-wmd-wrapper">
|
||||||
|
<div class="wmd-panel">
|
||||||
|
<div id="{{ id|safe }}_wmd_button_bar"></div>
|
||||||
|
<textarea{{ attrs|safe }}>{{ body }}</textarea>
|
||||||
|
</div>
|
||||||
|
{% if show_preview %}
|
||||||
|
<div id="{{ id|safe }}_wmd_preview" class="wmd-panel wmd-preview content-description"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
@ -46,4 +46,5 @@
|
|||||||
{% if REQUIRE_JAX %}
|
{% if REQUIRE_JAX %}
|
||||||
{% include "mathjax-load.html" %}
|
{% include "mathjax-load.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% include "comments/math.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -346,3 +346,8 @@
|
|||||||
{% include "comments/list.html" %}
|
{% include "comments/list.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block bodyend %}
|
||||||
|
{{ super() }}
|
||||||
|
{% include "comments/math.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
@ -42,8 +42,7 @@
|
|||||||
#edit-form {
|
#edit-form {
|
||||||
border: unset;
|
border: unset;
|
||||||
background: unset;
|
background: unset;
|
||||||
display: block;
|
max-width: 700px;
|
||||||
padding-bottom: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings {
|
.settings {
|
||||||
|
Loading…
Reference in New Issue
Block a user