diff --git a/.gitignore b/.gitignore index 35dd01e9..d45acfbf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,5 @@ backup.sh configs/*.json static/* gulp/res/js/socket.io.js -gulp/res/css/codethemes - +/gulp/res/css/codethemes tmp/ diff --git a/configs/main.json.example b/configs/main.json.example index 673a4d47..411de806 100644 --- a/configs/main.json.example +++ b/configs/main.json.example @@ -21,6 +21,7 @@ "highlightOptions": { "languageSubset": [ "javascript", + "js", "typescript", "java", "kotlin", diff --git a/gulp/res/css/codethemes b/gulp/res/css/codethemes deleted file mode 120000 index 0248f589..00000000 --- a/gulp/res/css/codethemes +++ /dev/null @@ -1 +0,0 @@ -/home/tom/jschan/node_modules/highlight.js/styles \ No newline at end of file diff --git a/gulp/res/js/progress.js b/gulp/res/js/progress.js index 2e6c85a5..bbbed44d 100644 --- a/gulp/res/js/progress.js +++ b/gulp/res/js/progress.js @@ -42,6 +42,7 @@ window.addEventListener('DOMContentLoaded', () => { fileLabel.innerText = 'Upload/Drop/Paste file(s)'; } else { fileLabel.innerText = `${files.length} files selected`; + //window.URL.createObjectURL(file); //todo make x marks to remove each one with "removeFile" } } @@ -105,6 +106,8 @@ window.addEventListener('DOMContentLoaded', () => { form.addEventListener('submit', function(event) { + submit.disabled = true; //prevent clicking post more than once + if (files && files.length > 0) { //add files to file input element const filesToUpload = new DataTransfer(); @@ -112,7 +115,6 @@ window.addEventListener('DOMContentLoaded', () => { filesToUpload.items.add(files[i]); } fileInput.files = filesToUpload.files; -//TODO: switch this line to workaround https://stackoverflow.com/a/46780880 - http://archive.is/niUVU } if (isBanners || localStorage.getItem('live') != 'true') { @@ -120,7 +122,6 @@ window.addEventListener('DOMContentLoaded', () => { } event.preventDefault(); - submit.disabled = true; //prevent clicking post more than once const xhr = new XMLHttpRequest(); diff --git a/helpers/posting/markdown.js b/helpers/posting/markdown.js index d42b5704..16c3f10d 100644 --- a/helpers/posting/markdown.js +++ b/helpers/posting/markdown.js @@ -11,18 +11,20 @@ const greentextRegex = /^>((?!>).+)/gm , spoilerRegex = /\|\|([\s\S]+?)\|\|/gm , detectedRegex = /(\(\(\(.+?\)\)\))/gm , linkRegex = /https?\://[^\s<>\[\]{}|\\^]+/g - , codeRegex = /```([\s\S]+?)```/gm + , codeRegex = /(?:(?[a-z+]{1,10})\r?\n)?(?[\s\S]+)/i + , splitRegex = /```([\s\S]+?)```/gm + , trimNewlineRegex = /^\s*(\r?\n)*|(\r?\n)*$/g , diceRegex = /##(?\d+)d(?\d+)(?:(?[+-])(?\d+))?/gmi , getDomain = (string) => string.split(/\/\/|\//)[1] //unused atm , diceRoll = require(__dirname+'/diceroll.js') , escape = require(__dirname+'/escape.js') - , { highlightAuto } = require('highlight.js') + , { highlight, highlightAuto } = require('highlight.js') , { highlightOptions } = require(__dirname+'/../../configs/main.json'); module.exports = { markdown: (text) => { - const chunks = text.split(codeRegex); + const chunks = text.split(splitRegex); for (let i = 0; i < chunks.length; i++) { //every other chunk will be a code block if (i % 2 === 0) { @@ -37,13 +39,22 @@ module.exports = { }, processCodeChunk: (text) => { - const trimFix = text.replace(/^\s*(\r?\n)*|(\r?\n)*$/g, ''); //remove extra whitespace/newlines at ends - const { language, relevance, value } = highlightAuto(trimFix, highlightOptions.languageSubset); - if (relevance > highlightOptions.threshold) { - return `possible language: ${language}, relevance: ${relevance}\n${value}`; - } else { - return `${escape(trimFix)}`; + const matches = text.match(codeRegex); + const trimFix = matches.groups.code.replace(trimNewlineRegex, ''); + let lang; + if (matches.groups.language && matches.groups.language.length > 0) { + lang = matches.groups.language.toLowerCase(); } + if (!lang) { + const { language, relevance, value } = highlightAuto(trimFix, highlightOptions.languageSubset); + if (relevance > highlightOptions.threshold) { + return `possible language: ${language}, relevance: ${relevance}\n${value}`; + } + } else if (lang !== 'plain' && highlightOptions.languageSubset.includes(lang)) { + const { value } = highlight(lang, trimFix); + return `language: ${lang}\n${value}`; + } + return `${escape(trimFix)}`; }, processRegularChunk: (text) => { diff --git a/views/custompages/faq.pug b/views/custompages/faq.pug index 08052e22..db0d61ad 100644 --- a/views/custompages/faq.pug +++ b/views/custompages/faq.pug @@ -142,17 +142,6 @@ block content td (((detected))) td span.detected (((detected))) - tr - td - | ``` - | code block - | ``` - td - span.code code block - tr - td `inline monospace` - td - span.mono inline monospace tr td https://example.com td: a(href='#!') https://example.com @@ -165,6 +154,21 @@ block content tr td >>>/b/123 td: a(class="quote" href="#!") >>>/b/123 + tr + td `inline monospace` + td + span.mono inline monospace + tr + td + | ```language + br + | code block + br + | ``` + td + span.code code block + tr + td(colspan=2) The "language" of code blocks is optional. Without it, automatic language detection is used. If the language is "plain", highlighting is disabled for the code block. Not all languages are supported, a subset of popular languages is used. If the language is not in the supported list, the code block will be rendered like "plain" with no highlighting. .table-container.flex-center.mv-5 .anchor#moderation table