Right-To-Left Support

This commit is contained in:
Martin Mahner 2018-12-19 15:03:15 +01:00
parent 3bfb153b74
commit 0f5fbd28f4
10 changed files with 99 additions and 17 deletions

View file

@ -46,6 +46,26 @@ if (wordwrapCheckbox && snippetDiv) {
wordwrapCheckbox.onchange = toggleWordwrap; wordwrapCheckbox.onchange = toggleWordwrap;
} }
// -----------------------------------------------------------------------------
// Right-To-Left
// -----------------------------------------------------------------------------
const rtlCheckbox = document.getElementById('id_rtl');
const snippetArea = document.getElementById('id_content');
function toggleRTL() {
if (rtlCheckbox.checked) {
snippetArea.dir = 'rtl';
} else {
snippetArea.dir = '';
}
}
if (rtlCheckbox && snippetArea) {
toggleRTL();
rtlCheckbox.onchange = toggleRTL;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Line Highlighting // Line Highlighting
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View file

@ -8,7 +8,14 @@
.snippet-form { .snippet-form {
background-color: $bgColor; background-color: $bgColor;
label { display: none; } label {
display: none;
font-size: 13px;
}
.options-rtl label {
display: inline;
}
select { select {
-moz-appearance: none; -moz-appearance: none;

View file

@ -20,24 +20,37 @@ def get_expire_values(expires):
else: else:
expire_type = Snippet.EXPIRE_TIME expire_type = Snippet.EXPIRE_TIME
expires = expires and expires or config.EXPIRE_DEFAULT expires = expires and expires or config.EXPIRE_DEFAULT
expires = datetime.datetime.now() + datetime.timedelta(seconds=int(expires)) expires = datetime.datetime.now() + datetime.timedelta(
seconds=int(expires)
)
return expires, expire_type return expires, expire_type
class SnippetForm(forms.ModelForm): class SnippetForm(forms.ModelForm):
content = forms.CharField( content = forms.CharField(
label=_('Content'), label=_('Content'),
widget=forms.Textarea(attrs={'placeholder': _('Awesome code goes here...')}), widget=forms.Textarea(
attrs={'placeholder': _('Awesome code goes here...')}
),
max_length=config.MAX_CONTENT_LENGTH, max_length=config.MAX_CONTENT_LENGTH,
strip=False, strip=False,
) )
lexer = forms.ChoiceField( lexer = forms.ChoiceField(
label=_('Lexer'), initial=LEXER_DEFAULT, choices=LEXER_CHOICES label=_('Lexer'),
initial=LEXER_DEFAULT,
choices=LEXER_CHOICES
) )
expires = forms.ChoiceField( expires = forms.ChoiceField(
label=_('Expires'), choices=config.EXPIRE_CHOICES, initial=config.EXPIRE_DEFAULT label=_('Expires'),
choices=config.EXPIRE_CHOICES,
initial=config.EXPIRE_DEFAULT,
)
rtl = forms.BooleanField(
label=_('Right to Left'),
required=False
) )
# Honeypot field # Honeypot field
@ -49,7 +62,7 @@ class SnippetForm(forms.ModelForm):
class Meta: class Meta:
model = Snippet model = Snippet
fields = ('content', 'lexer') fields = ('content', 'lexer', 'rtl')
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
super(SnippetForm, self).__init__(*args, **kwargs) super(SnippetForm, self).__init__(*args, **kwargs)

View file

@ -34,13 +34,14 @@ class Highlighter(object):
return l[1] return l[1]
return fallback return fallback
def render(self, code_string, lexer_name, **kwargs): def render(self, code_string, lexer_name, direction=None, **kwargs):
highlighted_string = self.highlight(code_string, lexer_name=lexer_name) highlighted_string = self.highlight(code_string, lexer_name=lexer_name)
context = { context = {
'highlighted': highlighted_string, 'highlighted': highlighted_string,
'highlighted_splitted': highlighted_string.splitlines(), 'highlighted_splitted': highlighted_string.splitlines(),
'lexer_name': lexer_name, 'lexer_name': lexer_name,
'lexer_display_name': self.get_lexer_display_name(lexer_name), 'lexer_display_name': self.get_lexer_display_name(lexer_name),
'direction': direction,
} }
context.update(kwargs) context.update(kwargs)
return render_to_string(self.template_name, context) return render_to_string(self.template_name, context)
@ -76,7 +77,9 @@ class MarkdownHighlighter(PlainTextHighlighter):
return mark_safe( return mark_safe(
misaka.html( misaka.html(
code_string, extensions=self.extensions, render_flags=self.render_flags code_string,
extensions=self.extensions,
render_flags=self.render_flags
) )
) )

View file

@ -0,0 +1,18 @@
# Generated by Django 2.1.1 on 2018-12-19 13:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dpaste', '0006_auto_20180622_1051'),
]
operations = [
migrations.AddField(
model_name='snippet',
name='rtl',
field=models.BooleanField(default=False, verbose_name='Right-to-left'),
),
]

View file

@ -22,7 +22,10 @@ def generate_secret_id(length):
) )
secret_id = ''.join( secret_id = ''.join(
[R.choice(config.SLUG_CHOICES) for i in range(length or config.SLUG_LENGTH)] [
R.choice(config.SLUG_CHOICES)
for i in range(length or config.SLUG_LENGTH)
]
) )
# Check if this slug already exists, if not, return this new slug # Check if this slug already exists, if not, return this new slug
@ -51,13 +54,16 @@ class Snippet(models.Model):
_('Secret ID'), max_length=255, blank=True, null=True, unique=True _('Secret ID'), max_length=255, blank=True, null=True, unique=True
) )
content = models.TextField(_('Content')) content = models.TextField(_('Content'))
lexer = models.CharField(_('Lexer'), max_length=30, default=highlight.LEXER_DEFAULT) lexer = models.CharField(
_('Lexer'), max_length=30, default=highlight.LEXER_DEFAULT
)
published = models.DateTimeField(_('Published'), auto_now_add=True) published = models.DateTimeField(_('Published'), auto_now_add=True)
expire_type = models.PositiveSmallIntegerField( expire_type = models.PositiveSmallIntegerField(
_('Expire Type'), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0] _('Expire Type'), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0]
) )
expires = models.DateTimeField(_('Expires'), blank=True, null=True) expires = models.DateTimeField(_('Expires'), blank=True, null=True)
view_count = models.PositiveIntegerField(_('View count'), default=0) view_count = models.PositiveIntegerField(_('View count'), default=0)
rtl = models.BooleanField(_('Right-to-left'), default=False)
parent = models.ForeignKey( parent = models.ForeignKey(
'self', 'self',
null=True, null=True,
@ -80,11 +86,17 @@ class Snippet(models.Model):
super(Snippet, self).save(*args, **kwargs) super(Snippet, self).save(*args, **kwargs)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('snippet_details', kwargs={'snippet_id': self.secret_id}) return reverse(
'snippet_details', kwargs={'snippet_id': self.secret_id}
)
def highlight(self): def highlight(self):
HighlighterClass = highlight.get_highlighter_class(self.lexer) HighlighterClass = highlight.get_highlighter_class(self.lexer)
return HighlighterClass().render(self.content, self.lexer) return HighlighterClass().render(
code_string=self.content,
lexer_name=self.lexer,
direction='rtl' if self.rtl else 'ltr',
)
@property @property
def lexer_name(self): def lexer_name(self):

View file

@ -1,5 +1,5 @@
<div class="snippet-text"> <div class="snippet-text">
<article> <article dir="{{ direction }}">
<div>{{ highlighted }}</div> <div>{{ highlighted }}</div>
</article> </article>
</div> </div>

View file

@ -18,6 +18,14 @@
{{ form.expires }} {{ form.expires }}
</p> </p>
<p class="options-rtl">
{{ form.rtl }}
<label for="{{ form.rtl.auto_id }}">
{% trans "Right-to-Left" %}
<small>[beta]</small>
</label>
</p>
<p class="action"> <p class="action">
<button class="btn" type="submit"> <button class="btn" type="submit">
{% trans "Paste Snippet" %} {% trans "Paste Snippet" %}

View file

@ -39,7 +39,6 @@ class SnippetView(FormView):
""" """
Create a new snippet. Create a new snippet.
""" """
form_class = SnippetForm form_class = SnippetForm
template_name = 'dpaste/new.html' template_name = 'dpaste/new.html'
@ -100,7 +99,11 @@ class SnippetDetailView(SnippetView, DetailView):
def get_initial(self): def get_initial(self):
snippet = self.get_object() snippet = self.get_object()
return {'content': snippet.content, 'lexer': snippet.lexer} return {
'content': snippet.content,
'lexer': snippet.lexer,
'rtl': snippet.rtl,
}
def form_valid(self, form): def form_valid(self, form):
snippet = form.save(parent=self.get_object()) snippet = form.save(parent=self.get_object())

View file

@ -7,11 +7,9 @@
"postinstall": "npm run build", "postinstall": "npm run build",
"start": "npm run build && pipenv run ./manage.py runserver", "start": "npm run build && pipenv run ./manage.py runserver",
"docs": "pipenv run sphinx-build -c docs docs docs/_build/html", "docs": "pipenv run sphinx-build -c docs docs docs/_build/html",
"build-css": "node-sass --output-style compressed -o build client/scss/dpaste.scss ", "build-css": "node-sass --output-style compressed -o build client/scss/dpaste.scss ",
"build-js": "uglifyjs --compress=\"drop_console=true,ecma=6\" --mangle=\"toplevel\" --output=build/dpaste.js client/js/dpaste.js", "build-js": "uglifyjs --compress=\"drop_console=true,ecma=6\" --mangle=\"toplevel\" --output=build/dpaste.js client/js/dpaste.js",
"build": "npm run build-css && npm run build-js", "build": "npm run build-css && npm run build-js",
"watch-css": "npm run build && node-sass --source-map true -o build/ --watch client/scss/dpaste.scss", "watch-css": "npm run build && node-sass --source-map true -o build/ --watch client/scss/dpaste.scss",
"watch-docs": "pipenv run sphinx-autobuild -c docs docs docs/_build/html" "watch-docs": "pipenv run sphinx-autobuild -c docs docs docs/_build/html"
}, },