From a01e351e898b880d337f305a0b4c28e7597770ea Mon Sep 17 00:00:00 2001
From: Tom Price "+text+" in RML
* Add OL processing to RML
* Fix a bug with squares appearing around the last page number
* Remove rml formatting in event_detail
* Improve handling of code blocks in RML
* Add MD to rigboard
Reduce MD title sizes as they were offensively large
* Add parsing of markdown when editing event items
* Improved list handling in RML
* Add tests for markdown support.
Focuses mainly on RML as that's where it will break
* Add indications of where MD support is enabled as per comment by @samozzy in #178.
Isn't quite a full description, but for the most part this should be enough for the people who know how to use it see where they can use it.
* Add failing test for markdown processing none
* Fix for failing test in e0d56e
* Add failing test for using single line breaks as per comment on #214
* Enable line break extension for single breaks in paragraphs by new lines.
Pass tests in ef3de607c39bb1a8152dcfa8974e68ae56a24a6d
* Enable GH flavour linebreaks in JS rendered markdown
* Made RML bullets pretty :)
* Added WYSIWYG editor. Works for notes & description, fails miserably for items :(
* Fixed for event items. Will probably fail tests because selenium can't type in simpleMDE :(
* FIX: Re-enable markdown on paperwork
Strikethrough is broken in all sorts of places for whatever reason
* FEAT: Markdown support on asset comments
* FIX: Prevent js injection through markdown fields
* Initial fixes
* Basic dark theme for simplemde
* Swap to locally delivered SimpleMDE
* Region for selenium testing of SimpleMDE
Bleh, Javascript all around
* Tests passing!
Fixed not using region for item modal, and overflow error on paperwork with really long description. Looks junk but I'm not really bothered
* Pep8 fixes
* Fallback for null HCapatcha sitekey
I.e. when we're on a branch
* Fix item description print being broken
* Actually fix sitekey problem
* Fixes for using markdown in asset comments
* Properly initialise markdown on asset comments
Co-authored-by: David Taylor
"}return'"+(escaped?code:escape(code,true))+"\n
\n"};Renderer.prototype.blockquote=function(quote){return"'+(escaped?code:escape(code,true))+"\n\n"+quote+"
\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"
\n":"
\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+""+type+">\n"};Renderer.prototype.listitem=function(text){return"\n"+"\n"+header+"\n"+"\n"+body+"\n"+"
\n"};Renderer.prototype.tablerow=function(content){return"\n"+content+" \n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+""+type+">\n"};Renderer.prototype.strong=function(text){return""+text+""};Renderer.prototype.em=function(text){return""+text+""};Renderer.prototype.codespan=function(text){return""+text+""};Renderer.prototype.br=function(){return this.options.xhtml?"
":"
"};Renderer.prototype.del=function(text){return""+text+""};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='"+text+"";return out};Renderer.prototype.image=function(href,title,text){var out='":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i
"+escape(e.message+"",true)+""}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}()); \ No newline at end of file diff --git a/RIGS/templates/event_detail.html b/RIGS/templates/event_detail.html index a0afc50e..b04089fb 100644 --- a/RIGS/templates/event_detail.html +++ b/RIGS/templates/event_detail.html @@ -1,5 +1,7 @@ {% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} +{% load markdown_tags %} + {% block content %}
{{ event.notes|linebreaksbr }}
+{{ event.notes|markdown }}
{% endif %} should become
+ for c in soup('code'):
+ c.name = 'font'
+ c['face'] = "Courier"
+
+ # blockquotes don't exist but we can still do something to show
+ for bq in soup('blockquote'):
+ bq.name = 'pre'
+ bq.string = bq.text
+
+ for alist in soup.find_all(['ul', 'ol']):
+ alist['style'] = alist.name
+ for li in alist.find_all('li', recursive=False):
+ text = li.find(text=True)
+ text.wrap(soup.new_tag('p'))
+
+ if alist.parent.name != 'li':
+ indent = soup.new_tag('indent')
+ indent['left'] = '0.6cm'
+
+ alist.wrap(indent)
+
+ # Paragraphs have a different tag
+ for p in soup('p'):
+ p.name = 'para'
+
+ return mark_safe(str(soup))
diff --git a/RIGS/tests/pages.py b/RIGS/tests/pages.py
index e458ecf1..f588fd12 100644
--- a/RIGS/tests/pages.py
+++ b/RIGS/tests/pages.py
@@ -96,7 +96,7 @@ class CreateEvent(FormPage):
_warning_selector = (By.XPATH, '/html/body/div[1]/div[1]')
form_items = {
- 'description': (regions.TextBox, (By.ID, 'id_description')),
+ 'description': (regions.SimpleMDETextArea, (By.ID, 'id_description')),
'name': (regions.TextBox, (By.ID, 'id_name')),
'start_date': (regions.DatePicker, (By.ID, 'id_start_date')),
@@ -110,7 +110,7 @@ class CreateEvent(FormPage):
'collected_by': (regions.TextBox, (By.ID, 'id_collector')),
'po': (regions.TextBox, (By.ID, 'id_purchase_order')),
- 'notes': (regions.TextBox, (By.ID, 'id_notes'))
+ 'notes': (regions.SimpleMDETextArea, (By.ID, 'id_notes'))
}
def select_event_type(self, type_name):
diff --git a/RIGS/tests/regions.py b/RIGS/tests/regions.py
index be7d9b7e..4ae75032 100644
--- a/RIGS/tests/regions.py
+++ b/RIGS/tests/regions.py
@@ -1,7 +1,7 @@
from pypom import Region
from selenium.webdriver.common.by import By
-from PyRIGS.tests.regions import TextBox, Modal
+from PyRIGS.tests.regions import TextBox, Modal, SimpleMDETextArea
class Header(Region):
@@ -42,7 +42,7 @@ class ItemModal(Modal):
form_items = {
'name': (TextBox, (By.ID, 'item_name')),
- 'description': (TextBox, (By.ID, 'item_description')),
+ 'description': (SimpleMDETextArea, (By.ID, 'item_description')),
'quantity': (TextBox, (By.ID, 'item_quantity')),
'price': (TextBox, (By.ID, 'item_cost'))
}
diff --git a/RIGS/tests/test_unit.py b/RIGS/tests/test_unit.py
index e7af6737..12fcf1a4 100644
--- a/RIGS/tests/test_unit.py
+++ b/RIGS/tests/test_unit.py
@@ -3,6 +3,8 @@ from datetime import date
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase
from django.test.utils import override_settings
+from django.utils.safestring import SafeText
+from RIGS.templatetags.markdown_tags import markdown_filter
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from pytest_django.asserts import assertRedirects, assertNotContains, assertContains
@@ -170,6 +172,7 @@ class TestInvoiceDelete(TestCase):
def setUpTestData(cls):
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
is_active=True, is_staff=True)
+ cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
cls.events = {
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
2: models.Event.objects.create(name="TE E2", start_date=date.today())
@@ -363,6 +366,215 @@ def test_checklist_review(admin_client, admin_user, checklist):
def test_ra_redirect(admin_client, admin_user, ra):
request_url = reverse('event_ra', kwargs={'pk': ra.event.pk})
expected_url = reverse('ra_edit', kwargs={'pk': ra.pk})
-
response = admin_client.get(request_url, follow=True)
assertRedirects(response, expected_url, status_code=302, target_status_code=200)
+
+
+class TestMarkdownTemplateTags(TestCase):
+ markdown = """
+An h1 header
+============
+
+Paragraphs are separated by a blank line.
+
+2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
+look like:
+
+ * this one
+ * that one
+ * the other one
+
+Note that --- not considering the asterisk --- the actual text
+content starts at 4-columns in.
+
+> Block quotes are
+> written like so.
+>
+> They can span multiple paragraphs,
+> if you like.
+
+Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
+in chapters 12--14"). Three dots ... will be converted to an ellipsis.
+Unicode is supported.
+
+
+
+An h2 header
+------------
+
+Here's a numbered list:
+
+ 1. first item
+ 2. second item
+ 3. third item
+
+Note again how the actual text starts at 4 columns in (4 characters
+from the left side). Here's a code sample:
+
+ # Let me re-iterate ...
+ for i in 1 .. 10 { do-something(i) }
+
+As you probably guessed, indented 4 spaces. By the way, instead of
+indenting the block, you can use delimited blocks, if you like:
+
+~~~
+define foobar() {
+ print "Welcome to flavor country!";
+}
+~~~
+
+(which makes copying & pasting easier). You can optionally mark the
+delimited block for Pandoc to syntax highlight it:
+
+~~~python
+import time
+# Quick, count to ten!
+for i in range(10):
+ # (but not *too* quick)
+ time.sleep(0.5)
+ print i
+~~~
+
+
+
+### An h3 header ###
+
+Now a nested list:
+
+ 1. First, get these ingredients:
+
+ * carrots
+ * celery
+ * lentils
+
+ 2. Boil some water.
+
+ 3. Dump everything in the pot and follow
+ this algorithm:
+
+ find wooden spoon
+ uncover pot
+ stir
+ cover pot
+ balance wooden spoon precariously on pot handle
+ wait 10 minutes
+ goto first step (or shut off burner when done)
+
+ Do not bump wooden spoon or it will fall.
+
+Notice again how text always lines up on 4-space indents (including
+that last line which continues item 3 above).
+
+Here's a link to [a website](http://foo.bar). Here's a footnote [^1].
+
+[^1]: Footnote text goes here.
+
+Tables can look like this:
+
+size material color
+---- ------------ ------------
+9 leather brown
+10 hemp canvas natural
+11 glass transparent
+
+Table: Shoes, their sizes, and what they're made of
+
+(The above is the caption for the table.) Pandoc also supports
+multi-line tables:
+
+-------- -----------------------
+keyword text
+-------- -----------------------
+red Sunsets, apples, and
+ other red or reddish
+ things.
+
+green Leaves, grass, frogs
+ and other things it's
+ not easy being.
+-------- -----------------------
+
+A horizontal rule follows.
+
+***
+
+Here's a definition list:
+
+apples
+ : Good for making applesauce.
+oranges
+ : Citrus!
+tomatoes
+ : There's no "e" in tomatoe.
+
+Again, text is indented 4 spaces. (Put a blank line between each
+term/definition pair to spread things out more.)
+
+Here's a "line block":
+
+| Line one
+| Line too
+| Line tree
+
+and images can be specified like so:
+
+
+
+Inline math equations go in like so: $\\omega = d\\phi / dt$. Display
+math should get its own line and be put in in double-dollarsigns:
+
+$$I = \\int \rho R^{2} dV$$
+
+And note that you can backslash-escape any punctuation characters
+which you wish to be displayed literally, ex.: \\`foo\\`, \\*bar\\*, etc.
+ """
+
+ def test_html_safe(self):
+ html = markdown_filter(self.markdown)
+ self.assertIsInstance(html, SafeText)
+
+ def test_img_strip(self):
+ rml = markdown_filter(self.markdown, 'rml')
+ self.assertNotIn("
monospace', rml)
+
+ def test_blockquote(self):
+ rml = markdown_filter(self.markdown, 'rml')
+ self.assertIn("\nBlock quotes", rml)
+
+ def test_lists(self):
+ rml = markdown_filter(self.markdown, 'rml')
+ self.assertIn("second item ", rml) #
+ self.assertIn("that one ", rml) #
+
+ def test_in_print(self):
+ event = models.Event.objects.create(
+ name="MD Print Test",
+ description=self.markdown,
+ start_date='2016-01-01',
+ )
+ user = models.Profile.objects.create(
+ username='RML test',
+ is_superuser=True, # Don't care about permissions
+ is_active=True,
+ )
+ user.set_password('rmltester')
+ user.save()
+
+ self.assertTrue(self.client.login(username=user.username, password='rmltester'))
+ response = self.client.get(reverse('event_print', kwargs={'pk': event.pk}))
+ self.assertEqual(response.status_code, 200)
+ # By the time we have a PDF it should be larger than the original by some margin
+ # RML hard fails if something doesn't work
+ self.assertGreater(len(response.content), len(self.markdown))
+
+ def test_nonetype(self):
+ html = markdown_filter(None)
+ self.assertIsNone(html)
+
+ def test_linebreaks(self):
+ html = markdown_filter(self.markdown)
+ self.assertIn("Itemized lists
\nlook like", html)
diff --git a/assets/templates/asset_form.html b/assets/templates/asset_form.html
index 57766f98..4a2380b9 100644
--- a/assets/templates/asset_form.html
+++ b/assets/templates/asset_form.html
@@ -5,11 +5,14 @@
{% block css %}
{{ block.super }}
+
{% endblock %}
{% block preload_js %}
{{ block.super }}
+
+
{% endblock %}
{% block js %}
@@ -72,6 +75,11 @@
preserveSelected: false
});
+
{% endblock %}
{% block content %}
diff --git a/assets/templates/partials/asset_detail_form.html b/assets/templates/partials/asset_detail_form.html
index 24de43b1..970bfce9 100644
--- a/assets/templates/partials/asset_detail_form.html
+++ b/assets/templates/partials/asset_detail_form.html
@@ -1,4 +1,6 @@
{% load widget_tweaks %}
+{% load markdown_tags %}
+
Asset Details
@@ -38,14 +40,14 @@
- {% render_field form.comments|add_class:'form-control' %}
+ {% render_field form.comments|add_class:'form-control md-enabled' %}
{% else %}
- Asset ID
- {{ object.asset_id }}
- Description
- - {{ object.description }}
+ - {{ object.description }}
- Category
- {{ object.category }}
@@ -57,7 +59,7 @@
- {{ object.serial_number|default:'-' }}
- Comments
- - {{ object.comments|default:'-'|linebreaksbr }}
+ - {{ object.comments|default:'-'|markdown }}
{% endif %}
diff --git a/assets/tests/pages.py b/assets/tests/pages.py
index ff5d081c..de47ebb6 100644
--- a/assets/tests/pages.py
+++ b/assets/tests/pages.py
@@ -77,7 +77,7 @@ class AssetForm(FormPage):
'description': (regions.TextBox, (By.ID, 'id_description')),
'is_cable': (regions.CheckBox, (By.ID, 'id_is_cable')),
'serial_number': (regions.TextBox, (By.ID, 'id_serial_number')),
- 'comments': (regions.TextBox, (By.ID, 'id_comments')),
+ 'comments': (regions.SimpleMDETextArea, (By.ID, 'id_comments')),
'purchase_price': (regions.TextBox, (By.ID, 'id_purchase_price')),
'salvage_value': (regions.TextBox, (By.ID, 'id_salvage_value')),
'date_acquired': (regions.DatePicker, (By.ID, 'id_date_acquired')),
diff --git a/gulpfile.js b/gulpfile.js
index 7fdc097c..e0eb064b 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -27,7 +27,9 @@ function styles(done) {
'node_modules/fullcalendar/main.css',
'node_modules/bootstrap-select/dist/css/bootstrap-select.css',
'node_modules/ajax-bootstrap-select/dist/css/ajax-bootstrap-select.css',
- 'node_modules/flatpickr/dist/flatpickr.css',])
+ 'node_modules/flatpickr/dist/flatpickr.css',
+ 'node_modules/simplemde/dist/simplemde.min.css'
+ ])
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(gulpif(function(file) { return bs_select.includes(file.relative);}, con('selects.css')))
@@ -62,6 +64,7 @@ function scripts() {
'node_modules/fullcalendar/main.js',
'node_modules/bootstrap-select/dist/js/bootstrap-select.js',
'node_modules/ajax-bootstrap-select/dist/js/ajax-bootstrap-select.js',
+ 'node_modules/simplemde/dist/simplemde.min.js',
'node_modules/konami/konami.js',
'pipeline/source_assets/js/**/*.js',])
.pipe(gulpif(function(file) { return base_scripts.includes(file.relative);}, con('base.js')))
diff --git a/package-lock.json b/package-lock.json
index 74fe15fa..07405a93 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -965,6 +965,19 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
+ "codemirror": {
+ "version": "5.65.0",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.0.tgz",
+ "integrity": "sha512-gWEnHKEcz1Hyz7fsQWpK7P0sPI2/kSkRX2tc7DFA6TmZuDN75x/1ejnH/Pn8adYKrLEA1V2ww6L00GudHZbSKw=="
+ },
+ "codemirror-spell-checker": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
+ "integrity": "sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=",
+ "requires": {
+ "typo-js": "*"
+ }
+ },
"collection-map": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
@@ -3691,6 +3704,11 @@
"object-visit": "^1.0.0"
}
},
+ "marked": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.8.tgz",
+ "integrity": "sha512-dkpJMIlJpc833hbjjg8jraw1t51e/eKDoG8TFOgc5O0Z77zaYKigYekTDop5AplRoKFGIaoazhYEhGkMtU3IeA=="
+ },
"matchdep": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
@@ -5641,6 +5659,16 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
"integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="
},
+ "simplemde": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz",
+ "integrity": "sha1-ojo12XjSxA7wfewAjJLwcNjggOM=",
+ "requires": {
+ "codemirror": "*",
+ "codemirror-spell-checker": "*",
+ "marked": "*"
+ }
+ },
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -6445,6 +6473,11 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
+ "typo-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.1.tgz",
+ "integrity": "sha512-bTGLjbD3WqZDR3CgEFkyi9Q/SS2oM29ipXrWfDb4M74ea69QwKAECVceYpaBu0GfdnASMg9Qfl67ttB23nePHg=="
+ },
"ua-parser-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz",
diff --git a/package.json b/package.json
index bcedc98b..0ed004a7 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"node-sass": "^7.0.0",
"popper.js": "^1.16.1",
"postcss": "^8.4.5",
+ "simplemde": "^1.11.2",
"uglify-js": "^3.14.5"
},
"devDependencies": {
diff --git a/pipeline/source_assets/js/interaction.js b/pipeline/source_assets/js/interaction.js
index a362ec3f..59124599 100644
--- a/pipeline/source_assets/js/interaction.js
+++ b/pipeline/source_assets/js/interaction.js
@@ -1,3 +1,7 @@
+marked.setOptions({
+ breaks: true,
+})
+
function setupItemTable(items_json) {
objectitems = JSON.parse(items_json)
$.each(objectitems, function (key, val) {
@@ -6,12 +10,12 @@ function setupItemTable(items_json) {
newitem = -1;
}
-function nl2br (str, is_xhtml) {
+function nl2br(str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
}
-function escapeHtml (str) {
+function escapeHtml(str) {
return $('').text(str).html();
}
@@ -32,6 +36,16 @@ function updatePrices() {
$('#total').text(parseFloat(sum + vat).toFixed(2));
}
+function setupMDE(selector) {
+ editor = new SimpleMDE({
+ element: $(selector)[0],
+ forceSync: true,
+ toolbar: ["bold", "italic", "strikethrough", "|", "unordered-list", "ordered-list", "|", "link", "|", "preview", "guide"],
+ status: true,
+ });
+ $(selector).data('mde_editor',editor);
+}
+
$('#item-table').on('click', '.item-delete', function () {
delete objectitems[$(this).data('pk')]
$('#item-' + $(this).data('pk')).remove();
@@ -106,7 +120,7 @@ $('body').on('submit', '#item-form', function (e) {
// update the table
$row = $('#item-' + pk);
$row.find('.name').html(escapeHtml(fields.name));
- $row.find('.description').html(nl2br(escapeHtml(fields.description)));
+ $row.find('.description').html(marked(fields.description));
$row.find('.cost').html(parseFloat(fields.cost).toFixed(2));
$row.find('.quantity').html(fields.quantity);
diff --git a/pipeline/source_assets/scss/dark_screen.scss b/pipeline/source_assets/scss/dark_screen.scss
index a00f014a..7296ffcb 100644
--- a/pipeline/source_assets/scss/dark_screen.scss
+++ b/pipeline/source_assets/scss/dark_screen.scss
@@ -133,4 +133,21 @@
-webkit-box-shadow: 0 0 0px 1000px rgba($info, .3) inset;
transition: background-color 5000s ease-in-out 0s;
}
+ .editor-toolbar > a {
+ color: white !important;
+ }
+ .editor-toolbar > a:hover {
+ background: transparent !important;
+ }
+ .editor-toolbar > a.active {
+ background: $info !important;
+ }
+ .cm-s-paper {
+ color: white;
+ background-color: $darktheme;
+ border-color: #bbb;
+ }
+ .CodeMirror-cursor {
+ border-color: white !important;
+ }
}
diff --git a/pipeline/source_assets/scss/screen.scss b/pipeline/source_assets/scss/screen.scss
index 71130573..a92f4428 100644
--- a/pipeline/source_assets/scss/screen.scss
+++ b/pipeline/source_assets/scss/screen.scss
@@ -226,3 +226,33 @@ html.embedded {
max-width: 3em;
}
}
+
+.markdown {
+ h1 {
+ font-size: $h1-font-size * 0.75;
+ }
+ h2 {
+ font-size: $h2-font-size * 0.8;
+ }
+ h3 {
+ font-size: $h3-font-size * 0.85;
+ }
+ h4 {
+ font-size: $h4-font-size * 0.9;
+ }
+ h5 {
+ font-size: $h5-font-size * 0.95;
+ }
+
+ img {
+ max-width: 100%;
+ }
+}
+
+#rigboard {
+ .markdown {
+ img {
+ max-width: 30rem;
+ }
+ }
+}
diff --git a/templates/base.html b/templates/base.html
index aaa3d106..ead9909a 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -77,8 +77,11 @@
+
+
{% include 'partials/dark_theme.html' %}
+
{% block js %}
{% endblock %}