From 0f5fbd28f4d17c09c3a73590372b3374a9543798 Mon Sep 17 00:00:00 2001
From: Martin Mahner
Date: Wed, 19 Dec 2018 15:03:15 +0100
Subject: [PATCH] Right-To-Left Support
---
client/js/dpaste.js | 20 ++++++++++++++++++
client/scss/components/_form.scss | 9 +++++++-
dpaste/forms.py | 23 ++++++++++++++++-----
dpaste/highlight.py | 7 +++++--
dpaste/migrations/0007_snippet_rtl.py | 18 ++++++++++++++++
dpaste/models.py | 20 ++++++++++++++----
dpaste/templates/dpaste/highlight/text.html | 2 +-
dpaste/templates/dpaste/includes/form.html | 8 +++++++
dpaste/views.py | 7 +++++--
package.json | 2 --
10 files changed, 99 insertions(+), 17 deletions(-)
create mode 100644 dpaste/migrations/0007_snippet_rtl.py
diff --git a/client/js/dpaste.js b/client/js/dpaste.js
index 5c656ea..f03d890 100644
--- a/client/js/dpaste.js
+++ b/client/js/dpaste.js
@@ -46,6 +46,26 @@ if (wordwrapCheckbox && snippetDiv) {
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
// -----------------------------------------------------------------------------
diff --git a/client/scss/components/_form.scss b/client/scss/components/_form.scss
index 4eebbab..cf40fc1 100644
--- a/client/scss/components/_form.scss
+++ b/client/scss/components/_form.scss
@@ -8,7 +8,14 @@
.snippet-form {
background-color: $bgColor;
- label { display: none; }
+ label {
+ display: none;
+ font-size: 13px;
+ }
+
+ .options-rtl label {
+ display: inline;
+ }
select {
-moz-appearance: none;
diff --git a/dpaste/forms.py b/dpaste/forms.py
index 4091a0b..a88fe5c 100644
--- a/dpaste/forms.py
+++ b/dpaste/forms.py
@@ -20,24 +20,37 @@ def get_expire_values(expires):
else:
expire_type = Snippet.EXPIRE_TIME
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
class SnippetForm(forms.ModelForm):
content = forms.CharField(
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,
strip=False,
)
lexer = forms.ChoiceField(
- label=_('Lexer'), initial=LEXER_DEFAULT, choices=LEXER_CHOICES
+ label=_('Lexer'),
+ initial=LEXER_DEFAULT,
+ choices=LEXER_CHOICES
)
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
@@ -49,7 +62,7 @@ class SnippetForm(forms.ModelForm):
class Meta:
model = Snippet
- fields = ('content', 'lexer')
+ fields = ('content', 'lexer', 'rtl')
def __init__(self, request, *args, **kwargs):
super(SnippetForm, self).__init__(*args, **kwargs)
diff --git a/dpaste/highlight.py b/dpaste/highlight.py
index 4e7a2b7..386b116 100644
--- a/dpaste/highlight.py
+++ b/dpaste/highlight.py
@@ -34,13 +34,14 @@ class Highlighter(object):
return l[1]
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)
context = {
'highlighted': highlighted_string,
'highlighted_splitted': highlighted_string.splitlines(),
'lexer_name': lexer_name,
'lexer_display_name': self.get_lexer_display_name(lexer_name),
+ 'direction': direction,
}
context.update(kwargs)
return render_to_string(self.template_name, context)
@@ -76,7 +77,9 @@ class MarkdownHighlighter(PlainTextHighlighter):
return mark_safe(
misaka.html(
- code_string, extensions=self.extensions, render_flags=self.render_flags
+ code_string,
+ extensions=self.extensions,
+ render_flags=self.render_flags
)
)
diff --git a/dpaste/migrations/0007_snippet_rtl.py b/dpaste/migrations/0007_snippet_rtl.py
new file mode 100644
index 0000000..f4e5299
--- /dev/null
+++ b/dpaste/migrations/0007_snippet_rtl.py
@@ -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'),
+ ),
+ ]
diff --git a/dpaste/models.py b/dpaste/models.py
index c27a716..b5f15e8 100644
--- a/dpaste/models.py
+++ b/dpaste/models.py
@@ -22,7 +22,10 @@ def generate_secret_id(length):
)
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
@@ -51,13 +54,16 @@ class Snippet(models.Model):
_('Secret ID'), max_length=255, blank=True, null=True, unique=True
)
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)
expire_type = models.PositiveSmallIntegerField(
_('Expire Type'), choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0]
)
expires = models.DateTimeField(_('Expires'), blank=True, null=True)
view_count = models.PositiveIntegerField(_('View count'), default=0)
+ rtl = models.BooleanField(_('Right-to-left'), default=False)
parent = models.ForeignKey(
'self',
null=True,
@@ -80,11 +86,17 @@ class Snippet(models.Model):
super(Snippet, self).save(*args, **kwargs)
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):
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
def lexer_name(self):
diff --git a/dpaste/templates/dpaste/highlight/text.html b/dpaste/templates/dpaste/highlight/text.html
index 29fa987..db56b23 100644
--- a/dpaste/templates/dpaste/highlight/text.html
+++ b/dpaste/templates/dpaste/highlight/text.html
@@ -1,5 +1,5 @@
\ No newline at end of file
diff --git a/dpaste/templates/dpaste/includes/form.html b/dpaste/templates/dpaste/includes/form.html
index fbb1bf5..2fb541b 100644
--- a/dpaste/templates/dpaste/includes/form.html
+++ b/dpaste/templates/dpaste/includes/form.html
@@ -18,6 +18,14 @@
{{ form.expires }}
+
+ {{ form.rtl }}
+
+ {% trans "Right-to-Left" %}
+ [beta]
+
+
+
{% trans "Paste Snippet" %}
diff --git a/dpaste/views.py b/dpaste/views.py
index b036f5a..c2ec679 100644
--- a/dpaste/views.py
+++ b/dpaste/views.py
@@ -39,7 +39,6 @@ class SnippetView(FormView):
"""
Create a new snippet.
"""
-
form_class = SnippetForm
template_name = 'dpaste/new.html'
@@ -100,7 +99,11 @@ class SnippetDetailView(SnippetView, DetailView):
def get_initial(self):
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):
snippet = form.save(parent=self.get_object())
diff --git a/package.json b/package.json
index f7b565a..a28f370 100644
--- a/package.json
+++ b/package.json
@@ -7,11 +7,9 @@
"postinstall": "npm run build",
"start": "npm run build && pipenv run ./manage.py runserver",
"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-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",
-
"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"
},