From 38e8a881d53207fbbbcec3a485a5d6ffb1c871db Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 16 Jul 2019 19:05:47 +0100 Subject: [PATCH] More refactoring to easily handle multiple renderers --- ElectronClient/app/compile.js | 2 +- ElectronClient/app/gui/NoteRevisionViewer.jsx | 2 +- ElectronClient/app/gui/NoteText.jsx | 42 ++++++++++-------- .../lib/components/note-body-viewer.js | 2 +- ReactNativeClient/lib/markdownUtils.js | 2 +- ReactNativeClient/lib/renderers/HtmlToHtml.js | 43 +++++++++++++++++++ .../lib/renderers/MarkupToHtml.js | 35 +++++++++++++++ .../lib/{ => renderers}/MdToHtml.js | 4 +- .../MdToHtml/rules/checkbox.js | 2 +- .../MdToHtml/rules/code_inline.js | 0 .../MdToHtml/rules/highlight_keywords.js | 2 +- .../MdToHtml/rules/html_image.js | 2 +- .../{ => renderers}/MdToHtml/rules/image.js | 2 +- .../{ => renderers}/MdToHtml/rules/katex.js | 0 .../MdToHtml/rules/link_open.js | 2 +- .../{ => renderers}/MdToHtml/setupLinkify.js | 0 .../lib/{MdToHtml => renderers}/noteStyle.js | 0 .../lib/{MdToHtml => renderers}/utils.js | 0 .../lib/{MdToHtml => renderers}/webviewLib.js | 0 Tools/buildReactNativeInjectedJs.js | 2 +- 20 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 ReactNativeClient/lib/renderers/HtmlToHtml.js create mode 100644 ReactNativeClient/lib/renderers/MarkupToHtml.js rename ReactNativeClient/lib/{ => renderers}/MdToHtml.js (98%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/checkbox.js (99%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/code_inline.js (100%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/highlight_keywords.js (98%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/html_image.js (98%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/image.js (96%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/katex.js (100%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/rules/link_open.js (98%) rename ReactNativeClient/lib/{ => renderers}/MdToHtml/setupLinkify.js (100%) rename ReactNativeClient/lib/{MdToHtml => renderers}/noteStyle.js (100%) rename ReactNativeClient/lib/{MdToHtml => renderers}/utils.js (100%) rename ReactNativeClient/lib/{MdToHtml => renderers}/webviewLib.js (100%) diff --git a/ElectronClient/app/compile.js b/ElectronClient/app/compile.js index aaff1a5ee..4d8254e05 100644 --- a/ElectronClient/app/compile.js +++ b/ElectronClient/app/compile.js @@ -47,7 +47,7 @@ convertJsx(__dirname + '/plugins'); const libContent = [ fs.readFileSync(basePath + '/ReactNativeClient/lib/string-utils-common.js', 'utf8'), fs.readFileSync(basePath + '/ReactNativeClient/lib/markJsUtils.js', 'utf8'), - fs.readFileSync(basePath + '/ReactNativeClient/lib/MdToHtml/webviewLib.js', 'utf8'), + fs.readFileSync(basePath + '/ReactNativeClient/lib/renderers/webviewLib.js', 'utf8'), ]; fs.writeFileSync(__dirname + '/gui/note-viewer/lib.js', libContent.join('\n'), 'utf8'); diff --git a/ElectronClient/app/gui/NoteRevisionViewer.jsx b/ElectronClient/app/gui/NoteRevisionViewer.jsx index f6452a1fc..02617ec28 100644 --- a/ElectronClient/app/gui/NoteRevisionViewer.jsx +++ b/ElectronClient/app/gui/NoteRevisionViewer.jsx @@ -9,7 +9,7 @@ const Revision = require('lib/models/Revision'); const Setting = require('lib/models/Setting'); const RevisionService = require('lib/services/RevisionService'); const shared = require('lib/components/shared/note-screen-shared.js'); -const MdToHtml = require('lib/MdToHtml'); +const MdToHtml = require('lib/renderers/MdToHtml'); const { time } = require('lib/time-utils.js'); const ReactTooltip = require('react-tooltip'); const { substrWithEllipsis } = require('lib/string-utils'); diff --git a/ElectronClient/app/gui/NoteText.jsx b/ElectronClient/app/gui/NoteText.jsx index c5f61b07b..61263a027 100644 --- a/ElectronClient/app/gui/NoteText.jsx +++ b/ElectronClient/app/gui/NoteText.jsx @@ -12,7 +12,7 @@ const TagList = require('./TagList.min.js'); const { connect } = require('react-redux'); const { _ } = require('lib/locale.js'); const { reg } = require('lib/registry.js'); -const MdToHtml = require('lib/MdToHtml'); +const MarkupToHtml = require('lib/renderers/MarkupToHtml'); const shared = require('lib/components/shared/note-screen-shared.js'); const { bridge } = require('electron').remote.require('./bridge'); const { themeStyle } = require('../theme.js'); @@ -325,12 +325,12 @@ class NoteTextComponent extends React.Component { } } - mdToHtml() { - if (this.mdToHtml_) return this.mdToHtml_; - this.mdToHtml_ = new MdToHtml({ + markupToHtml() { + if (this.markupToHtml_) return this.markupToHtml_; + this.markupToHtml_ = new MarkupToHtml({ resourceBaseUrl: 'file://' + Setting.value('resourceDir') + '/', }); - return this.mdToHtml_; + return this.markupToHtml_; } async componentWillMount() { @@ -355,7 +355,7 @@ class NoteTextComponent extends React.Component { this.lastLoadedNoteId_ = note ? note.id : null; - this.updateHtml(note && note.body ? note.body : ''); + this.updateHtml(note ? note.markup_language : null, note && note.body ? note.body : ''); eventManager.on('alarmChange', this.onAlarmChange_); eventManager.on('noteTypeToggle', this.onNoteTypeToggle_); @@ -369,7 +369,7 @@ class NoteTextComponent extends React.Component { componentWillUnmount() { this.saveIfNeeded(); - this.mdToHtml_ = null; + this.markupToHtml_ = null; eventManager.removeListener('alarmChange', this.onAlarmChange_); eventManager.removeListener('noteTypeToggle', this.onNoteTypeToggle_); @@ -481,7 +481,7 @@ class NoteTextComponent extends React.Component { } } - this.mdToHtml_ = null; + this.markupToHtml_ = null; // If we are loading nothing (noteId == null), make sure to // set webviewReady to false too because the webview component @@ -595,7 +595,7 @@ class NoteTextComponent extends React.Component { // if (newState.note) await shared.refreshAttachedResources(this, newState.note.body); - this.updateHtml(newState.note ? newState.note.body : ''); + this.updateHtml(newState.note ? newState.note.markup_language : null, newState.note ? newState.note.body : ''); } async componentWillReceiveProps(nextProps) { @@ -930,12 +930,20 @@ class NoteTextComponent extends React.Component { } } - async updateHtml(body = null, options = null) { + async updateHtml(markupLanguage = null, body = null, options = null) { if (!options) options = {}; if (!('useCustomCss' in options)) options.useCustomCss = true; let bodyToRender = body; - if (bodyToRender === null) bodyToRender = this.state.note && this.state.note.body ? this.state.note.body : ''; + + if (bodyToRender === null) { + bodyToRender = this.state.note && this.state.note.body ? this.state.note.body : ''; + markupLanguage = this.state.note ? this.state.note.markup_language : Note.MARKUP_LANGUAGE_MARKDOWN; + } + + if (!markupLanguage) markupLanguage = Note.MARKUP_LANGUAGE_MARKDOWN; + + const resources = await shared.attachedResources(bodyToRender); const theme = themeStyle(this.props.theme); @@ -943,7 +951,7 @@ class NoteTextComponent extends React.Component { codeTheme: theme.codeThemeCss, postMessageSyntax: 'ipcProxySendToHost', userCss: options.useCustomCss ? this.props.customCss : '', - resources: await shared.attachedResources(bodyToRender), + resources: resources, codeHighlightCacheKey: this.state.note ? this.state.note.id : null, }; @@ -953,10 +961,10 @@ class NoteTextComponent extends React.Component { if (!bodyToRender.trim() && visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') < 0) { // Fixes https://github.com/laurent22/joplin/issues/217 - bodyToRender = '*' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + '*'; + bodyToRender = '' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + ''; } - const result = this.mdToHtml().render(bodyToRender, theme, mdOptions); + const result = this.markupToHtml().render(markupLanguage, bodyToRender, theme, mdOptions); this.setState({ bodyHtml: result.html, @@ -1079,7 +1087,7 @@ class NoteTextComponent extends React.Component { lastSavedNote: Object.assign({}, note), }); - this.updateHtml(note.body); + this.updateHtml(note.markup_language, note.body); } catch (error) { reg.logger().error(error); bridge().showErrorMessageBox(error.message); @@ -1108,13 +1116,13 @@ class NoteTextComponent extends React.Component { const previousTheme = Setting.value('theme'); Setting.setValue('theme', Setting.THEME_LIGHT); this.lastSetHtml_ = ''; - await this.updateHtml(tempBody, { useCustomCss: false }); + await this.updateHtml(this.state.note.markup_language, tempBody, { useCustomCss: false }); this.forceUpdate(); const restoreSettings = async () => { Setting.setValue('theme', previousTheme); this.lastSetHtml_ = ''; - await this.updateHtml(previousBody); + await this.updateHtml(this.state.note.markup_language, previousBody); this.forceUpdate(); } diff --git a/ReactNativeClient/lib/components/note-body-viewer.js b/ReactNativeClient/lib/components/note-body-viewer.js index 9a93595e1..9cf727ac0 100644 --- a/ReactNativeClient/lib/components/note-body-viewer.js +++ b/ReactNativeClient/lib/components/note-body-viewer.js @@ -6,7 +6,7 @@ const Resource = require('lib/models/Resource.js'); const Setting = require('lib/models/Setting.js'); const { reg } = require('lib/registry.js'); const { shim } = require('lib/shim'); -const MdToHtml = require('lib/MdToHtml.js'); +const MdToHtml = require('lib/renderers/MdToHtml.js'); const shared = require('lib/components/shared/note-screen-shared.js'); class NoteBodyViewer extends Component { diff --git a/ReactNativeClient/lib/markdownUtils.js b/ReactNativeClient/lib/markdownUtils.js index 8447426af..3c51e1dd8 100644 --- a/ReactNativeClient/lib/markdownUtils.js +++ b/ReactNativeClient/lib/markdownUtils.js @@ -1,7 +1,7 @@ const stringPadding = require('string-padding'); const urlUtils = require('lib/urlUtils'); const MarkdownIt = require('markdown-it'); -const setupLinkify = require('lib/MdToHtml/setupLinkify'); +const setupLinkify = require('lib/renderers/MdToHtml/setupLinkify'); const markdownUtils = { diff --git a/ReactNativeClient/lib/renderers/HtmlToHtml.js b/ReactNativeClient/lib/renderers/HtmlToHtml.js new file mode 100644 index 000000000..d9e309334 --- /dev/null +++ b/ReactNativeClient/lib/renderers/HtmlToHtml.js @@ -0,0 +1,43 @@ +const Resource = require('lib/models/Resource'); +const htmlUtils = require('lib/htmlUtils'); +const utils = require('./utils'); +const jsdom = require("jsdom"); +const { JSDOM } = jsdom; + +class HtmlToHtml { + + constructor(options) { + this.resourceBaseUrl_ = 'resourceBaseUrl' in options ? options.resourceBaseUrl : null; + } + + render(markup, theme, options) { + const dom = new JSDOM(markup); + + // Replace all the image resource IDs by path to local files + const imgs = dom.window.document.getElementsByTagName('img'); + for (const img of imgs) { + if (!img.src) continue; + const r = utils.imageReplacement(img.src, options.resources, this.resourceBaseUrl_); + if (!r) continue; + + if (typeof r === 'string') { + img.outerHTML = r; + } else { + for (const n in r) { + img.setAttribute(n, r[n]); + } + } + } + + // We need this extra style so that the images don't overflow + const extraStyle = '' + + return { + html: extraStyle + htmlUtils.headAndBodyHtml(dom.window.document), + cssFiles: [], + } + } + +} + +module.exports = HtmlToHtml; \ No newline at end of file diff --git a/ReactNativeClient/lib/renderers/MarkupToHtml.js b/ReactNativeClient/lib/renderers/MarkupToHtml.js new file mode 100644 index 000000000..7e2800332 --- /dev/null +++ b/ReactNativeClient/lib/renderers/MarkupToHtml.js @@ -0,0 +1,35 @@ +const MdToHtml = require('./MdToHtml'); +const HtmlToHtml = require('./HtmlToHtml'); +const Note = require('lib/models/Note'); + +class MarkupToHtml { + + constructor(options) { + this.options_ = options; + this.renderers_ = {}; + } + + renderer(markupLanguage) { + if (this.renderers_[markupLanguage]) return this.renderers_[markupLanguage]; + + let RendererClass = null; + + if (markupLanguage === Note.MARKUP_LANGUAGE_MARKDOWN) { + RendererClass = MdToHtml; + } else if (markupLanguage === Note.MARKUP_LANGUAGE_HTML) { + RendererClass = HtmlToHtml; + } else { + throw new Error('Invalid markup language: ' + markupLanguage); + } + + this.renderers_[markupLanguage] = new RendererClass(this.options_); + return this.renderers_[markupLanguage]; + } + + render(markupLanguage, markup, theme, options) { + return this.renderer(markupLanguage).render(markup, theme, options); + } + +} + +module.exports = MarkupToHtml; \ No newline at end of file diff --git a/ReactNativeClient/lib/MdToHtml.js b/ReactNativeClient/lib/renderers/MdToHtml.js similarity index 98% rename from ReactNativeClient/lib/MdToHtml.js rename to ReactNativeClient/lib/renderers/MdToHtml.js index 52e31a2d8..e8f0901b1 100644 --- a/ReactNativeClient/lib/MdToHtml.js +++ b/ReactNativeClient/lib/renderers/MdToHtml.js @@ -6,8 +6,8 @@ const { shim } = require('lib/shim.js'); const { _ } = require('lib/locale'); const md5 = require('md5'); const StringUtils = require('lib/string-utils.js'); -const noteStyle = require('./MdToHtml/noteStyle'); -const Setting = require('./models/Setting.js'); +const noteStyle = require('./noteStyle'); +const Setting = require('lib/models/Setting.js'); const rules = { image: require('./MdToHtml/rules/image'), checkbox: require('./MdToHtml/rules/checkbox'), diff --git a/ReactNativeClient/lib/MdToHtml/rules/checkbox.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/checkbox.js similarity index 99% rename from ReactNativeClient/lib/MdToHtml/rules/checkbox.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/checkbox.js index bfc8a1e43..f059b170b 100644 --- a/ReactNativeClient/lib/MdToHtml/rules/checkbox.js +++ b/ReactNativeClient/lib/renderers/MdToHtml/rules/checkbox.js @@ -1,7 +1,7 @@ const Entities = require('html-entities').AllHtmlEntities; const htmlentities = (new Entities()).encode; const Resource = require('lib/models/Resource.js'); -const utils = require('../utils'); +const utils = require('../../utils'); let checkboxIndex_ = -1; diff --git a/ReactNativeClient/lib/MdToHtml/rules/code_inline.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/code_inline.js similarity index 100% rename from ReactNativeClient/lib/MdToHtml/rules/code_inline.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/code_inline.js diff --git a/ReactNativeClient/lib/MdToHtml/rules/highlight_keywords.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/highlight_keywords.js similarity index 98% rename from ReactNativeClient/lib/MdToHtml/rules/highlight_keywords.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/highlight_keywords.js index 49e8ff26f..7b5a622f8 100644 --- a/ReactNativeClient/lib/MdToHtml/rules/highlight_keywords.js +++ b/ReactNativeClient/lib/renderers/MdToHtml/rules/highlight_keywords.js @@ -1,7 +1,7 @@ const Entities = require('html-entities').AllHtmlEntities; const htmlentities = (new Entities()).encode; const Resource = require('lib/models/Resource.js'); -const utils = require('../utils'); +const utils = require('../../utils'); const StringUtils = require('lib/string-utils.js'); const md5 = require('md5'); diff --git a/ReactNativeClient/lib/MdToHtml/rules/html_image.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/html_image.js similarity index 98% rename from ReactNativeClient/lib/MdToHtml/rules/html_image.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/html_image.js index 5607af77b..9c0d0212c 100644 --- a/ReactNativeClient/lib/MdToHtml/rules/html_image.js +++ b/ReactNativeClient/lib/renderers/MdToHtml/rules/html_image.js @@ -2,7 +2,7 @@ const Entities = require('html-entities').AllHtmlEntities; const htmlentities = (new Entities()).encode; const Resource = require('lib/models/Resource.js'); const htmlUtils = require('lib/htmlUtils.js'); -const utils = require('../utils'); +const utils = require('../../utils'); function renderImageHtml(before, src, after, ruleOptions) { const r = utils.imageReplacement(src, ruleOptions.resources, ruleOptions.resourceBaseUrl); diff --git a/ReactNativeClient/lib/MdToHtml/rules/image.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/image.js similarity index 96% rename from ReactNativeClient/lib/MdToHtml/rules/image.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/image.js index c4b90b89c..95b61453c 100644 --- a/ReactNativeClient/lib/MdToHtml/rules/image.js +++ b/ReactNativeClient/lib/renderers/MdToHtml/rules/image.js @@ -1,7 +1,7 @@ const Entities = require('html-entities').AllHtmlEntities; const htmlentities = (new Entities()).encode; const Resource = require('lib/models/Resource.js'); -const utils = require('../utils'); +const utils = require('../../utils'); const htmlUtils = require('lib/htmlUtils.js'); function installRule(markdownIt, mdOptions, ruleOptions) { diff --git a/ReactNativeClient/lib/MdToHtml/rules/katex.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/katex.js similarity index 100% rename from ReactNativeClient/lib/MdToHtml/rules/katex.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/katex.js diff --git a/ReactNativeClient/lib/MdToHtml/rules/link_open.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/link_open.js similarity index 98% rename from ReactNativeClient/lib/MdToHtml/rules/link_open.js rename to ReactNativeClient/lib/renderers/MdToHtml/rules/link_open.js index d786c3fbe..7dd24fdab 100644 --- a/ReactNativeClient/lib/MdToHtml/rules/link_open.js +++ b/ReactNativeClient/lib/renderers/MdToHtml/rules/link_open.js @@ -1,7 +1,7 @@ const Entities = require('html-entities').AllHtmlEntities; const htmlentities = (new Entities()).encode; const Resource = require('lib/models/Resource.js'); -const utils = require('../utils'); +const utils = require('../../utils'); const loaderImage = ''; diff --git a/ReactNativeClient/lib/MdToHtml/setupLinkify.js b/ReactNativeClient/lib/renderers/MdToHtml/setupLinkify.js similarity index 100% rename from ReactNativeClient/lib/MdToHtml/setupLinkify.js rename to ReactNativeClient/lib/renderers/MdToHtml/setupLinkify.js diff --git a/ReactNativeClient/lib/MdToHtml/noteStyle.js b/ReactNativeClient/lib/renderers/noteStyle.js similarity index 100% rename from ReactNativeClient/lib/MdToHtml/noteStyle.js rename to ReactNativeClient/lib/renderers/noteStyle.js diff --git a/ReactNativeClient/lib/MdToHtml/utils.js b/ReactNativeClient/lib/renderers/utils.js similarity index 100% rename from ReactNativeClient/lib/MdToHtml/utils.js rename to ReactNativeClient/lib/renderers/utils.js diff --git a/ReactNativeClient/lib/MdToHtml/webviewLib.js b/ReactNativeClient/lib/renderers/webviewLib.js similarity index 100% rename from ReactNativeClient/lib/MdToHtml/webviewLib.js rename to ReactNativeClient/lib/renderers/webviewLib.js diff --git a/Tools/buildReactNativeInjectedJs.js b/Tools/buildReactNativeInjectedJs.js index e740e9503..21dfc2d8a 100644 --- a/Tools/buildReactNativeInjectedJs.js +++ b/Tools/buildReactNativeInjectedJs.js @@ -18,7 +18,7 @@ async function copyJs(name, filePath) { async function main(argv) { await fs.mkdirp(outputDir); - await copyJs('webviewLib', rnDir + '/lib/MdToHtml/webviewLib.js'); + await copyJs('webviewLib', rnDir + '/lib/renderers/MdToHtml/webviewLib.js'); } main(process.argv).catch((error) => {