diff --git a/src/components/gutters/NewComment.vue b/src/components/gutters/NewComment.vue index e6d05066..127c7161 100644 --- a/src/components/gutters/NewComment.vue +++ b/src/components/gutters/NewComment.vue @@ -80,7 +80,7 @@ export default { mounted() { const preElt = this.$el.querySelector('pre.markdown-highlighting'); const scrollerElt = this.$el.querySelector('.comment__text-inner'); - const clEditor = cledit(preElt, scrollerElt); + const clEditor = cledit(preElt, scrollerElt, true); clEditor.init({ sectionHighlighter: section => Prism.highlight( section.text, editorSvc.prismGrammars[section.data]), diff --git a/src/data/defaultFileProperties.yml b/src/data/defaultFileProperties.yml index 78113250..a61ff2f5 100644 --- a/src/data/defaultFileProperties.yml +++ b/src/data/defaultFileProperties.yml @@ -1,6 +1,6 @@ -# File properties can contain metadata used -# for your publications (Wordpress, Blogger...). -# For example: +## File properties can contain metadata used +## for your publications (Wordpress, Blogger...). +## For example: #title: My article #author: @@ -11,53 +11,47 @@ #status: draft #date: YYYY-MM-DD HH:MM:SS -# Extension configuration +## Extensions configuration +## Preset can be `default`, `commonmark` or `zero` +## Use preset `zero` to enable extensions manually. extensions: + preset: default - # Markdown extensions - markdown: - abbr: true - breaks: true - deflist: true - del: true - fence: true - footnote: true - linkify: true - sub: true - sup: true - table: true - typographer: true - # Enable strict CommonMark: - #abbr: false - #breaks: false - #deflist: false - #del: false - #footnote: false - #linkify: false - #sub: false - #sup: false - #table: false - #typographer: false + ## Markdown extensions + #markdown: + #abbr: true + #breaks: true + #deflist: true + #del: true + #fence: true + #footnote: true + #linkify: true + #sub: true + #sup: true + #table: true + #typographer: true - # Emoji extension - emoji: - # Enable support for emojis & emoticons - enabled: true - # Enable shortcuts like :) :-( - shortcuts: false + ## Emoji extension + #emoji: + ## Support for emojis & emoticons + #enabled: true - # Katex extension - # Render LaTeX mathematical expressions by using: - # $...$ for inline formulas - # $$...$$ for displayed formulas. - # See https://math.meta.stackexchange.com/questions/5020 - katex: - enabled: true + ## Shortcuts like :) :-( + ## Disabled in the default preset. + #shortcuts: false - # Mermaid extension - # Convert code blocks starting with: - # ```mermaid - # into diagrams and flowcharts. - # See https://mermaidjs.github.io/ - mermaid: - enabled: true + ## Katex extension + ## Render LaTeX mathematical expressions by using: + ## $...$ for inline formulas + ## $$...$$ for displayed formulas. + ## See https://math.meta.stackexchange.com/questions/5020 + #katex: + #enabled: true + + ## Mermaid extension + ## Convert code blocks starting with: + ## ```mermaid + ## into diagrams and flowcharts. + ## See https://mermaidjs.github.io/ + #mermaid: + #enabled: true diff --git a/src/data/defaultSettings.yml b/src/data/defaultSettings.yml index 292f5d4e..235dc7e7 100644 --- a/src/data/defaultSettings.yml +++ b/src/data/defaultSettings.yml @@ -49,7 +49,7 @@ wkhtmltopdf: marginRight: 25 marginBottom: 25 marginLeft: 25 - # A3, A4, Legal or Letter + # `A3`, `A4`, `Legal` or `Letter` pageSize: A4 # Options passed to pandoc diff --git a/src/data/presets.js b/src/data/presets.js new file mode 100644 index 00000000..14575017 --- /dev/null +++ b/src/data/presets.js @@ -0,0 +1,58 @@ +const zero = { + markdown: { + abbr: false, + breaks: false, + deflist: false, + del: false, + fence: false, + footnote: false, + linkify: false, + sub: false, + sup: false, + table: false, + typographer: false, + }, + emoji: { + enabled: false, + shortcuts: false, + }, + katex: { + enabled: false, + }, + mermaid: { + enabled: false, + }, +}; + +export default { + zero: [zero], + commonmark: [zero, { + markdown: { + fence: true, + }, + }], + default: [zero, { + markdown: { + abbr: true, + breaks: true, + deflist: true, + del: true, + fence: true, + footnote: true, + linkify: true, + sub: true, + sup: true, + table: true, + typographer: true, + }, + emoji: { + enabled: true, + }, + katex: { + enabled: true, + }, + mermaid: { + enabled: true, + }, + }], +}; diff --git a/src/services/cledit/cleditCore.js b/src/services/cledit/cleditCore.js index 7f724832..8ebb185f 100644 --- a/src/services/cledit/cleditCore.js +++ b/src/services/cledit/cleditCore.js @@ -3,7 +3,7 @@ import TurndownService from 'turndown/lib/turndown.browser.umd'; import htmlSanitizer from '../../libs/htmlSanitizer'; import store from '../../store'; -function cledit(contentElt, scrollEltOpt) { +function cledit(contentElt, scrollEltOpt, isMarkdown = false) { const scrollElt = scrollEltOpt || contentElt; const editor = { $contentElt: contentElt, @@ -314,25 +314,28 @@ function cledit(contentElt, scrollEltOpt) { }, 1); }); - contentElt.addEventListener('copy', (evt) => { - if (evt.clipboardData) { - evt.clipboardData.setData('text/plain', selectionMgr.getSelectedText()); - evt.preventDefault(); - } - }); + let turndownService; + if (isMarkdown) { + contentElt.addEventListener('copy', (evt) => { + if (evt.clipboardData) { + evt.clipboardData.setData('text/plain', selectionMgr.getSelectedText()); + evt.preventDefault(); + } + }); - contentElt.addEventListener('cut', (evt) => { - if (evt.clipboardData) { - evt.clipboardData.setData('text/plain', selectionMgr.getSelectedText()); - evt.preventDefault(); - replace(selectionMgr.selectionStart, selectionMgr.selectionEnd, ''); - } else { - undoMgr.setCurrentMode('single'); - } - adjustCursorPosition(); - }); + contentElt.addEventListener('cut', (evt) => { + if (evt.clipboardData) { + evt.clipboardData.setData('text/plain', selectionMgr.getSelectedText()); + evt.preventDefault(); + replace(selectionMgr.selectionStart, selectionMgr.selectionEnd, ''); + } else { + undoMgr.setCurrentMode('single'); + } + adjustCursorPosition(); + }); - const turndownService = new TurndownService(store.getters['data/computedSettings'].turndown); + turndownService = new TurndownService(store.getters['data/computedSettings'].turndown); + } contentElt.addEventListener('paste', (evt) => { undoMgr.setCurrentMode('single'); @@ -341,17 +344,19 @@ function cledit(contentElt, scrollEltOpt) { let clipboardData = evt.clipboardData; if (clipboardData) { data = clipboardData.getData('text/plain'); - try { - const html = clipboardData.getData('text/html'); - if (html && !clipboardData.getData('text/css')) { - const sanitizedHtml = htmlSanitizer.sanitizeHtml(html) - .replace(/ /g, ' '); // Replace non-breaking spaces with classic spaces - if (sanitizedHtml) { - data = turndownService.turndown(sanitizedHtml); + if (turndownService) { + try { + const html = clipboardData.getData('text/html'); + if (html && !clipboardData.getData('text/css')) { + const sanitizedHtml = htmlSanitizer.sanitizeHtml(html) + .replace(/ /g, ' '); // Replace non-breaking spaces with classic spaces + if (sanitizedHtml) { + data = turndownService.turndown(sanitizedHtml); + } } + } catch (e) { + // Ignore } - } catch (e) { - // Ignore } } else { clipboardData = window.clipboardData; diff --git a/src/services/editorSvcDiscussions.js b/src/services/editorSvcDiscussions.js index cd28b999..91bab96a 100644 --- a/src/services/editorSvcDiscussions.js +++ b/src/services/editorSvcDiscussions.js @@ -114,7 +114,7 @@ function reversePatches(patches) { export default { createClEditor(editorElt) { - this.clEditor = cledit(editorElt, editorElt.parentNode); + this.clEditor = cledit(editorElt, editorElt.parentNode, true); clEditor = this.clEditor; clEditor.on('contentChanged', (text) => { const oldContent = store.getters['content/current']; diff --git a/src/services/utils.js b/src/services/utils.js index c7f249cd..f74d4aa2 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -1,6 +1,7 @@ import yaml from 'js-yaml'; import '../libs/clunderscore'; -import defaultProperties from '../data/defaultFileProperties.yml'; +import defaultPropertiesYaml from '../data/defaultFileProperties.yml'; +import presets from '../data/presets'; const origin = `${location.protocol}//${location.host}`; @@ -23,9 +24,47 @@ const parseQueryParams = (params) => { return result; }; +// For utils.computeProperties() +const deepOverride = (obj, opt) => { + if (obj === undefined) { + return opt; + } + const objType = Object.prototype.toString.call(obj); + const optType = Object.prototype.toString.call(opt); + if (objType !== optType) { + return obj; + } + if (objType !== '[object Object]') { + return opt === undefined ? obj : opt; + } + Object.keys({ + ...obj, + ...opt, + }).forEach((key) => { + obj[key] = deepOverride(obj[key], opt[key]); + }); + return obj; +}; + // For utils.addQueryParams() const urlParser = document.createElement('a'); +const deepCopy = (obj) => { + if (obj == null) { + return obj; + } + return JSON.parse(JSON.stringify(obj)); +}; + +// Build presets +Object.keys(presets).forEach((key) => { + let preset = deepCopy(presets[key][0]); + if (presets[key][1]) { + preset = deepOverride(preset, presets[key][1]); + } + presets[key] = preset; +}); + export default { cleanTrashAfter: 7 * 24 * 60 * 60 * 1000, // 7 days origin, @@ -65,9 +104,7 @@ export default { sanitizeName(name) { return `${name || ''}`.slice(0, 250) || 'Untitled'; }, - deepCopy(obj) { - return obj == null ? obj : JSON.parse(JSON.stringify(obj)); - }, + deepCopy, serializeObject(obj) { return obj === undefined ? obj : JSON.stringify(obj, (key, value) => { if (Object.prototype.toString.call(value) !== '[object Object]') { @@ -118,26 +155,13 @@ export default { }, computeProperties(yamlProperties) { const customProperties = yaml.safeLoad(yamlProperties); - const properties = yaml.safeLoad(defaultProperties); - const override = (obj, opt) => { - const objType = Object.prototype.toString.call(obj); - const optType = Object.prototype.toString.call(opt); - if (obj === undefined) { - return opt; - } else if (objType !== optType) { - return obj; - } else if (objType !== '[object Object]') { - return opt === undefined ? obj : opt; - } - Object.keys({ - ...obj, - ...opt, - }).forEach((key) => { - obj[key] = override(obj[key], opt[key]); - }); - return obj; - }; - return override(properties, customProperties); + const defaultProperties = yaml.safeLoad(defaultPropertiesYaml); + const properties = deepOverride(defaultProperties, customProperties); + const preset = deepCopy(presets[properties.extensions.preset] || presets.default); + const extensions = deepOverride(preset, properties.extensions); + extensions.preset = properties.extensions.preset; + properties.extensions = extensions; + return properties; }, randomize(value) { return Math.floor((1 + (Math.random() * 0.2)) * value);