diff --git a/.eslintignore b/.eslintignore
index 069b17363..71a1c0380 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -50,3 +50,4 @@ ElectronClient/app/packageInfo.js
# Ignore files generated from TypeScript files
ElectronClient/app/gui/ShareNoteDialog.js
ReactNativeClient/lib/JoplinServerApi.js
+ReactNativeClient/pluginAssetsLoader.js
diff --git a/.gitignore b/.gitignore
index f930ed530..300905eb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@ Tools/commit_hook.txt
# Ignore files generated from TypeScript files
ElectronClient/app/gui/ShareNoteDialog.js
ReactNativeClient/lib/JoplinServerApi.js
+ReactNativeClient/pluginAssetsLoader.js
diff --git a/CliClient/package-lock.json b/CliClient/package-lock.json
index 32f0f9802..760c425d8 100644
--- a/CliClient/package-lock.json
+++ b/CliClient/package-lock.json
@@ -806,6 +806,11 @@
"debug": "^2.6.9"
}
},
+ "font-awesome-filetypes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/font-awesome-filetypes/-/font-awesome-filetypes-2.1.0.tgz",
+ "integrity": "sha512-U6hi14GRjfZFIWsTNyVmCBuHyPhiizWEKVbaQqHipKQv3rA1l1PNvmKulzpqxonFnQMToty5ZhfWbc/0IjLDGA=="
+ },
"for-each-property": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz",
@@ -959,6 +964,24 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
+ "handlebars": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
+ "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
+ "requires": {
+ "neo-async": "^2.6.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -1533,6 +1556,75 @@
"integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
"dev": true
},
+ "joplin-renderer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/joplin-renderer/-/joplin-renderer-1.0.4.tgz",
+ "integrity": "sha512-Q7GoEYA6hYtv8ZQgxccn/gSw25V8kQPyWdolK0PJhG7m2BJa0zunyagtaOhMkYuWl8beDYHwTsvsVx2ayKqMcQ==",
+ "requires": {
+ "base-64": "^0.1.0",
+ "font-awesome-filetypes": "^2.1.0",
+ "fs-extra": "^8.1.0",
+ "highlight.js": "^9.17.1",
+ "html-entities": "^1.2.1",
+ "katex": "^0.11.1",
+ "markdown-it": "^10.0.0",
+ "markdown-it-abbr": "^1.0.4",
+ "markdown-it-anchor": "^5.2.5",
+ "markdown-it-deflist": "^2.0.3",
+ "markdown-it-emoji": "^1.4.0",
+ "markdown-it-footnote": "^3.0.2",
+ "markdown-it-ins": "^3.0.0",
+ "markdown-it-mark": "^3.0.0",
+ "markdown-it-multimd-table": "^4.0.0",
+ "markdown-it-sub": "^1.0.0",
+ "markdown-it-sup": "^1.0.0",
+ "markdown-it-toc-done-right": "^4.1.0",
+ "md5": "^2.2.1",
+ "uslug": "^1.0.4"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+ "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
+ },
+ "highlight.js": {
+ "version": "9.17.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.17.1.tgz",
+ "integrity": "sha512-TA2/doAur5Ol8+iM3Ov7qy3jYcr/QiJ2eDTdRF4dfbjG7AaaB99J5G+zSl11ljbl6cIcahgPY6SKb3sC3EJ0fw==",
+ "requires": {
+ "handlebars": "^4.5.3"
+ }
+ },
+ "markdown-it": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
+ "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
+ "requires": {
+ "argparse": "^1.0.7",
+ "entities": "~2.0.0",
+ "linkify-it": "^2.0.0",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ }
+ }
+ },
"joplin-turndown": {
"version": "4.0.19",
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.19.tgz",
@@ -1636,6 +1728,21 @@
"resolved": "https://registry.npmjs.org/jssha/-/jssha-2.3.1.tgz",
"integrity": "sha1-FHshJTaQNcpLL30hDcU58Amz3po="
},
+ "katex": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.11.1.tgz",
+ "integrity": "sha512-5oANDICCTX0NqYIyAiFCCwjQ7ERu3DQG2JFHLbYOf+fXaMoH8eg/zOq5WSYJsKMi/QebW+Eh3gSM+oss1H/bww==",
+ "requires": {
+ "commander": "^2.19.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ }
+ }
+ },
"klaw": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
@@ -1755,6 +1862,64 @@
"uc.micro": "^1.0.5"
}
},
+ "markdown-it-abbr": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-it-abbr/-/markdown-it-abbr-1.0.4.tgz",
+ "integrity": "sha1-1mtTZFIcuz3Yqlna37ovtoZcj9g="
+ },
+ "markdown-it-anchor": {
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz",
+ "integrity": "sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ=="
+ },
+ "markdown-it-deflist": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/markdown-it-deflist/-/markdown-it-deflist-2.0.3.tgz",
+ "integrity": "sha512-/BNZ8ksW42bflm1qQLnRI09oqU2847Z7MVavrR0MORyKLtiUYOMpwtlAfMSZAQU9UCvaUZMpgVAqoS3vpToJxw=="
+ },
+ "markdown-it-emoji": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz",
+ "integrity": "sha1-m+4OmpkKljupbfaYDE/dsF37Tcw="
+ },
+ "markdown-it-footnote": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-3.0.2.tgz",
+ "integrity": "sha512-JVW6fCmZWjvMdDQSbOT3nnOQtd9iAXmw7hTSh26+v42BnvXeVyGMDBm5b/EZocMed2MbCAHiTX632vY0FyGB8A=="
+ },
+ "markdown-it-ins": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-3.0.0.tgz",
+ "integrity": "sha512-+vyAdBuMGwmT2yMlAFJSx2VR/0QZ1onQ/Mkkmr4l9tDFOh5sVoAgRbkgbuSsk+sxJ9vaMH/IQ323ydfvQrPO/Q=="
+ },
+ "markdown-it-mark": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-3.0.0.tgz",
+ "integrity": "sha512-HqMWeKfMMOu4zBO0emmxsoMWmbf2cPKZY1wP6FsTbKmicFfp5y4L3KXAsNeO1rM6NTJVOrNlLKMPjWzriBGspw=="
+ },
+ "markdown-it-multimd-table": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-multimd-table/-/markdown-it-multimd-table-4.0.0.tgz",
+ "integrity": "sha512-kdM3fH+/sRMfHQgD2CM1BcIpLNODUCuoiFr6TwS7mDJBYntVXDJxZbFwGDRflIc9ZzAfsUbr0lnHc6RbYafIsw==",
+ "requires": {
+ "markdown-it": "^8.4.2"
+ }
+ },
+ "markdown-it-sub": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz",
+ "integrity": "sha1-N1/WAm6ufdywEkl/ZBEZXqHjr+g="
+ },
+ "markdown-it-sup": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz",
+ "integrity": "sha1-y5yf+RpSVawI8/09YyhuFd8KH8M="
+ },
+ "markdown-it-toc-done-right": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.1.0.tgz",
+ "integrity": "sha512-UhD2Oj6cZV3ycYPoelt4hTkwKIK3zbPP1wjjdpCq7UGtWQOFalDFDv1s2zBYV6aR2gMs/X8kpJcOYsQmUbiXDw=="
+ },
"md5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
@@ -1911,6 +2076,11 @@
}
}
},
+ "neo-async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
+ },
"nextgen-events": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/nextgen-events/-/nextgen-events-1.3.0.tgz",
@@ -2089,6 +2259,15 @@
"wrappy": "1"
}
},
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ }
+ },
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -3205,6 +3384,11 @@
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
},
+ "unorm": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
+ "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
+ },
"unpack-string": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/unpack-string/-/unpack-string-0.0.2.tgz",
@@ -3244,6 +3428,14 @@
"requires-port": "^1.0.0"
}
},
+ "uslug": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/uslug/-/uslug-1.0.4.tgz",
+ "integrity": "sha1-uaIvCRTgqGFAYz2swwLl9PpFBnc=",
+ "requires": {
+ "unorm": ">= 1.0.0"
+ }
+ },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -3333,6 +3525,11 @@
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
},
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ },
"wordwrapjs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
diff --git a/CliClient/package.json b/CliClient/package.json
index bce8af198..3013a0bc8 100644
--- a/CliClient/package.json
+++ b/CliClient/package.json
@@ -44,6 +44,7 @@
"html-minifier": "^3.5.15",
"image-data-uri": "^2.0.0",
"image-type": "^3.0.0",
+ "joplin-renderer": "^1.0.4",
"joplin-turndown": "^4.0.19",
"joplin-turndown-plugin-gfm": "^1.0.11",
"jssha": "^2.3.0",
diff --git a/ElectronClient/app/app.js b/ElectronClient/app/app.js
index dd26548ce..e245fdb92 100644
--- a/ElectronClient/app/app.js
+++ b/ElectronClient/app/app.js
@@ -6,6 +6,7 @@ const Setting = require('lib/models/Setting.js');
const { shim } = require('lib/shim.js');
const MasterKey = require('lib/models/MasterKey');
const Note = require('lib/models/Note');
+const { MarkupToHtml } = require('joplin-renderer');
const { _, setLocale } = require('lib/locale.js');
const { Logger } = require('lib/logger.js');
const fs = require('fs-extra');
@@ -1132,7 +1133,7 @@ class Application extends BaseApplication {
for (const itemId of ['copy', 'paste', 'cut', 'selectAll', 'bold', 'italic', 'link', 'code', 'insertDateTime', 'commandStartExternalEditing', 'setTags', 'showLocalSearch']) {
const menuItem = Menu.getApplicationMenu().getMenuItemById(`edit:${itemId}`);
if (!menuItem) continue;
- menuItem.enabled = !!note && note.markup_language === Note.MARKUP_LANGUAGE_MARKDOWN;
+ menuItem.enabled = !!note && note.markup_language === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
}
const menuItem = Menu.getApplicationMenu().getMenuItemById('help:toggleDevTools');
diff --git a/ElectronClient/app/gui/NoteRevisionViewer.jsx b/ElectronClient/app/gui/NoteRevisionViewer.jsx
index 6fc3eafa1..61e787475 100644
--- a/ElectronClient/app/gui/NoteRevisionViewer.jsx
+++ b/ElectronClient/app/gui/NoteRevisionViewer.jsx
@@ -6,16 +6,16 @@ const NoteTextViewer = require('./NoteTextViewer.min');
const HelpButton = require('./HelpButton.min');
const BaseModel = require('lib/BaseModel');
const Revision = require('lib/models/Revision');
-const Note = require('lib/models/Note');
const urlUtils = require('lib/urlUtils');
const Setting = require('lib/models/Setting');
const RevisionService = require('lib/services/RevisionService');
const shared = require('lib/components/shared/note-screen-shared.js');
-const MarkupToHtml = require('lib/renderers/MarkupToHtml');
+const { MarkupToHtml } = require('joplin-renderer');
const { time } = require('lib/time-utils.js');
const ReactTooltip = require('react-tooltip');
const { urlDecode, substrWithEllipsis } = require('lib/string-utils');
const { bridge } = require('electron').remote.require('./bridge');
+const markupLanguageUtils = require('lib/markupLanguageUtils');
class NoteRevisionViewerComponent extends React.PureComponent {
constructor() {
@@ -101,7 +101,7 @@ class NoteRevisionViewerComponent extends React.PureComponent {
async reloadNote() {
let noteBody = '';
- let markupLanguage = Note.MARKUP_LANGUAGE_MARKDOWN;
+ let markupLanguage = MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
if (!this.state.revisions.length || !this.state.currentRevId) {
noteBody = _('This note has no history');
this.setState({ note: null });
@@ -116,18 +116,18 @@ class NoteRevisionViewerComponent extends React.PureComponent {
const theme = themeStyle(this.props.theme);
- const markupToHtml = new MarkupToHtml({
+ const markupToHtml = markupLanguageUtils.newMarkupToHtml({
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
});
- const result = markupToHtml.render(markupLanguage, noteBody, theme, {
+ const result = await markupToHtml.render(markupLanguage, noteBody, theme, {
codeTheme: theme.codeThemeCss,
userCss: this.props.customCss ? this.props.customCss : '',
resources: await shared.attachedResources(noteBody),
postMessageSyntax: 'ipcProxySendToHost',
});
- this.viewerRef_.current.wrappedInstance.send('setHtml', result.html, { cssFiles: result.cssFiles });
+ this.viewerRef_.current.wrappedInstance.send('setHtml', result.html, { cssFiles: result.cssFiles, pluginAssets: result.pluginAssets });
}
async webview_ipcMessage(event) {
diff --git a/ElectronClient/app/gui/NoteText.jsx b/ElectronClient/app/gui/NoteText.jsx
index 12e99ebf1..00cba4dce 100644
--- a/ElectronClient/app/gui/NoteText.jsx
+++ b/ElectronClient/app/gui/NoteText.jsx
@@ -15,7 +15,7 @@ const TagList = require('./TagList.min.js');
const { connect } = require('react-redux');
const { _ } = require('lib/locale.js');
const { reg } = require('lib/registry.js');
-const MarkupToHtml = require('lib/renderers/MarkupToHtml');
+const { MarkupToHtml } = require('joplin-renderer');
const shared = require('lib/components/shared/note-screen-shared.js');
const { bridge } = require('electron').remote.require('./bridge');
const { themeStyle } = require('../theme.js');
@@ -41,6 +41,7 @@ const SearchEngine = require('lib/services/SearchEngine');
const NoteTextViewer = require('./NoteTextViewer.min');
const NoteRevisionViewer = require('./NoteRevisionViewer.min');
const TemplateUtils = require('lib/TemplateUtils');
+const markupLanguageUtils = require('lib/markupLanguageUtils');
require('brace/mode/markdown');
// https://ace.c9.io/build/kitchen-sink.html
@@ -111,6 +112,7 @@ class NoteTextComponent extends React.Component {
newAndNoTitleChangeNoteId: null,
bodyHtml: '',
lastRenderCssFiles: [],
+ lastRenderPluginAssets: [],
lastKeys: [],
showLocalSearch: false,
localSearch: Object.assign({}, this.localSearchDefaultState),
@@ -401,9 +403,11 @@ class NoteTextComponent extends React.Component {
markupToHtml() {
if (this.markupToHtml_) return this.markupToHtml_;
- this.markupToHtml_ = new MarkupToHtml({
+
+ this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
});
+
return this.markupToHtml_;
}
@@ -1048,10 +1052,10 @@ class NoteTextComponent extends React.Component {
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;
+ markupLanguage = this.state.note ? this.state.note.markup_language : MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
}
- if (!markupLanguage) markupLanguage = Note.MARKUP_LANGUAGE_MARKDOWN;
+ if (!markupLanguage) markupLanguage = MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
const resources = await shared.attachedResources(bodyToRender);
@@ -1072,11 +1076,11 @@ class NoteTextComponent extends React.Component {
bodyToRender = `${_('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout'))} `;
}
- const result = this.markupToHtml().render(markupLanguage, bodyToRender, theme, mdOptions);
+ const result = await this.markupToHtml().render(markupLanguage, bodyToRender, theme, mdOptions);
this.setState({
bodyHtml: result.html,
- lastRenderCssFiles: result.cssFiles,
+ lastRenderPluginAssets: result.pluginAssets,
});
}
@@ -1630,7 +1634,7 @@ class NoteTextComponent extends React.Component {
});
}
- if (note.markup_language === Note.MARKUP_LANGUAGE_MARKDOWN && editorIsVisible) {
+ if (note.markup_language === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN && editorIsVisible) {
toolbarItems.push({
tooltip: _('Bold'),
iconName: 'fa-bold',
@@ -1858,7 +1862,7 @@ class NoteTextComponent extends React.Component {
const style = this.props.style;
const note = this.state.note;
const body = note && note.body ? note.body : '';
- const markupLanguage = note ? note.markup_language : Note.MARKUP_LANGUAGE_MARKDOWN;
+ const markupLanguage = note ? note.markup_language : MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
const theme = themeStyle(this.props.theme);
const visiblePanes = this.props.visiblePanes || ['editor', 'viewer'];
const isTodo = note && !!note.is_todo;
@@ -1999,7 +2003,7 @@ class NoteTextComponent extends React.Component {
const htmlHasChanged = this.lastSetHtml_ !== html;
if (htmlHasChanged) {
let options = {
- cssFiles: this.state.lastRenderCssFiles,
+ pluginAssets: this.state.lastRenderPluginAssets,
downloadResources: Setting.value('sync.resourceDownloadMode'),
};
this.webviewRef_.current.wrappedInstance.send('setHtml', html, options);
diff --git a/ElectronClient/app/gui/note-viewer/index.html b/ElectronClient/app/gui/note-viewer/index.html
index 5aaa9bcf7..d641ad21b 100644
--- a/ElectronClient/app/gui/note-viewer/index.html
+++ b/ElectronClient/app/gui/note-viewer/index.html
@@ -66,15 +66,21 @@
}
}));
- const loadedCssFiles_ = {};
- function loadCssFiles(cssFiles) {
- for (let i = 0; i < cssFiles.length; i++) {
- const f = cssFiles[i];
- if (loadedCssFiles_[f]) continue;
- const link = document.createElement('link');
- link.rel = 'stylesheet';
- link.href = f;
- document.getElementById('joplin-container-styleContainer').appendChild(link);
+ const loadedPluginAssets_ = {}
+ function loadPluginAssets(assets) {
+ if (!assets) return;
+
+ for (let i = 0; i < assets.length; i++) {
+ const asset = assets[i];
+ if (loadedPluginAssets_[asset.name]) continue;
+ loadedPluginAssets_[asset.name] = true;
+
+ if (asset.mime === 'text/css') {
+ const link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = 'pluginAssets/' + asset.name;
+ document.getElementById('joplin-container-styleContainer').appendChild(link);
+ }
}
}
@@ -154,7 +160,9 @@
}, 1);
}
- loadCssFiles(event.options.cssFiles);
+ // loadCssFiles(event.options.cssFiles);
+
+ loadPluginAssets(event.options.pluginAssets);
if (event.options.downloadResources === 'manual') {
webviewLib.setupResourceManualDownload();
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/highlight.js/atom-one-dark-reasonable.css b/ElectronClient/app/gui/note-viewer/pluginAssets/highlight.js/atom-one-dark-reasonable.css
new file mode 100644
index 000000000..0a43fe26b
--- /dev/null
+++ b/ElectronClient/app/gui/note-viewer/pluginAssets/highlight.js/atom-one-dark-reasonable.css
@@ -0,0 +1,75 @@
+/*
+
+Atom One Dark With support for ReasonML by Gidi Morris, based off work by Daniel Gamage
+
+Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
+
+*/
+.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 0.5em;
+ color: #abb2bf;
+ background: #282c34;
+}
+.hljs-keyword, .hljs-operator {
+ color: #F92672;
+}
+.hljs-pattern-match {
+ color: #F92672;
+}
+.hljs-pattern-match .hljs-constructor {
+ color: #61aeee;
+}
+.hljs-function {
+ color: #61aeee;
+}
+.hljs-function .hljs-params {
+ color: #A6E22E;
+}
+.hljs-function .hljs-params .hljs-typing {
+ color: #FD971F;
+}
+.hljs-module-access .hljs-module {
+ color: #7e57c2;
+}
+.hljs-constructor {
+ color: #e2b93d;
+}
+.hljs-constructor .hljs-string {
+ color: #9CCC65;
+}
+.hljs-comment, .hljs-quote {
+ color: #b18eb1;
+ font-style: italic;
+}
+.hljs-doctag, .hljs-formula {
+ color: #c678dd;
+}
+.hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst {
+ color: #e06c75;
+}
+.hljs-literal {
+ color: #56b6c2;
+}
+.hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string {
+ color: #98c379;
+}
+.hljs-built_in, .hljs-class .hljs-title {
+ color: #e6c07b;
+}
+.hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-type, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-number {
+ color: #d19a66;
+}
+.hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title {
+ color: #61aeee;
+}
+.hljs-emphasis {
+ font-style: italic;
+}
+.hljs-strong {
+ font-weight: bold;
+}
+.hljs-link {
+ text-decoration: underline;
+}
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/highlight.js/atom-one-light.css b/ElectronClient/app/gui/note-viewer/pluginAssets/highlight.js/atom-one-light.css
new file mode 100644
index 000000000..d5bd1d2a9
--- /dev/null
+++ b/ElectronClient/app/gui/note-viewer/pluginAssets/highlight.js/atom-one-light.css
@@ -0,0 +1,96 @@
+/*
+
+Atom One Light by Daniel Gamage
+Original One Light Syntax theme from https://github.com/atom/one-light-syntax
+
+base: #fafafa
+mono-1: #383a42
+mono-2: #686b77
+mono-3: #a0a1a7
+hue-1: #0184bb
+hue-2: #4078f2
+hue-3: #a626a4
+hue-4: #50a14f
+hue-5: #e45649
+hue-5-2: #c91243
+hue-6: #986801
+hue-6-2: #c18401
+
+*/
+
+.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 0.5em;
+ color: #383a42;
+ background: #fafafa;
+}
+
+.hljs-comment,
+.hljs-quote {
+ color: #a0a1a7;
+ font-style: italic;
+}
+
+.hljs-doctag,
+.hljs-keyword,
+.hljs-formula {
+ color: #a626a4;
+}
+
+.hljs-section,
+.hljs-name,
+.hljs-selector-tag,
+.hljs-deletion,
+.hljs-subst {
+ color: #e45649;
+}
+
+.hljs-literal {
+ color: #0184bb;
+}
+
+.hljs-string,
+.hljs-regexp,
+.hljs-addition,
+.hljs-attribute,
+.hljs-meta-string {
+ color: #50a14f;
+}
+
+.hljs-built_in,
+.hljs-class .hljs-title {
+ color: #c18401;
+}
+
+.hljs-attr,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-type,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo,
+.hljs-number {
+ color: #986801;
+}
+
+.hljs-symbol,
+.hljs-bullet,
+.hljs-link,
+.hljs-meta,
+.hljs-selector-id,
+.hljs-title {
+ color: #4078f2;
+}
+
+.hljs-emphasis {
+ font-style: italic;
+}
+
+.hljs-strong {
+ font-weight: bold;
+}
+
+.hljs-link {
+ text-decoration: underline;
+}
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Main-Regular.woff2 b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Main-Regular.woff2
new file mode 100644
index 000000000..e3f71eb7e
Binary files /dev/null and b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Main-Regular.woff2 differ
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Math-Italic.woff2 b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Math-Italic.woff2
new file mode 100644
index 000000000..e3ea522a6
Binary files /dev/null and b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Math-Italic.woff2 differ
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Size1-Regular.woff2 b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Size1-Regular.woff2
new file mode 100644
index 000000000..483e7b66e
Binary files /dev/null and b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Size1-Regular.woff2 differ
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Size2-Regular.woff2 b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Size2-Regular.woff2
new file mode 100644
index 000000000..5ff706067
Binary files /dev/null and b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/fonts/KaTeX_Size2-Regular.woff2 differ
diff --git a/ElectronClient/app/gui/note-viewer/pluginAssets/katex/katex.css b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/katex.css
new file mode 100644
index 000000000..c0cd1451a
--- /dev/null
+++ b/ElectronClient/app/gui/note-viewer/pluginAssets/katex/katex.css
@@ -0,0 +1 @@
+@font-face{font-family:KaTeX_AMS;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype");font-weight:400;font-style:normal}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important}.katex .katex-version:after{content:"0.11.1"}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathdefault{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:700;font-style:italic}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{width:0;position:relative}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:0 solid;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex svg path{stroke:none}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 .5em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:.04em solid}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left}
diff --git a/ElectronClient/app/package-lock.json b/ElectronClient/app/package-lock.json
index 0a38941a1..ac4aa67db 100644
--- a/ElectronClient/app/package-lock.json
+++ b/ElectronClient/app/package-lock.json
@@ -706,8 +706,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"aproba": {
"version": "1.2.0",
@@ -728,14 +727,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -750,20 +747,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -880,8 +874,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"ini": {
"version": "1.3.5",
@@ -893,7 +886,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -908,7 +900,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -916,14 +907,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -942,7 +931,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -1023,8 +1011,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -1036,7 +1023,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -1122,8 +1108,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -1159,7 +1144,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -1179,7 +1163,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -1223,14 +1206,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
}
}
},
@@ -1248,15 +1229,13 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
- "dev": true,
- "optional": true
+ "dev": true
},
"is-glob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"dev": true,
- "optional": true,
"requires": {
"is-extglob": "^1.0.0"
}
@@ -1658,8 +1637,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.0.tgz",
"integrity": "sha512-OElxJ1lUSinuoUnkpOgLmxp0DC4ytEhODEL6QJU0NpxE/mI4rUSh8h1P1Wkvfi3xQEBcxXR2gBIPNYNuaFcAbQ==",
- "dev": true,
- "optional": true
+ "dev": true
},
"boxen": {
"version": "3.2.0",
@@ -3588,15 +3566,13 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
- "dev": true,
- "optional": true
+ "dev": true
},
"is-glob": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"dev": true,
- "optional": true,
"requires": {
"is-extglob": "^1.0.0"
}
@@ -3721,6 +3697,24 @@
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
},
+ "handlebars": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
+ "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
+ "requires": {
+ "neo-async": "^2.6.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ }
+ }
+ },
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -3785,9 +3779,12 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"highlight.js": {
- "version": "9.15.6",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz",
- "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ=="
+ "version": "9.17.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.17.1.tgz",
+ "integrity": "sha512-TA2/doAur5Ol8+iM3Ov7qy3jYcr/QiJ2eDTdRF4dfbjG7AaaB99J5G+zSl11ljbl6cIcahgPY6SKb3sC3EJ0fw==",
+ "requires": {
+ "handlebars": "^4.5.3"
+ }
},
"hoist-non-react-statics": {
"version": "2.5.0",
@@ -4181,6 +4178,67 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
+ "joplin-renderer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/joplin-renderer/-/joplin-renderer-1.0.4.tgz",
+ "integrity": "sha512-Q7GoEYA6hYtv8ZQgxccn/gSw25V8kQPyWdolK0PJhG7m2BJa0zunyagtaOhMkYuWl8beDYHwTsvsVx2ayKqMcQ==",
+ "requires": {
+ "base-64": "^0.1.0",
+ "font-awesome-filetypes": "^2.1.0",
+ "fs-extra": "^8.1.0",
+ "highlight.js": "^9.17.1",
+ "html-entities": "^1.2.1",
+ "katex": "^0.11.1",
+ "markdown-it": "^10.0.0",
+ "markdown-it-abbr": "^1.0.4",
+ "markdown-it-anchor": "^5.2.5",
+ "markdown-it-deflist": "^2.0.3",
+ "markdown-it-emoji": "^1.4.0",
+ "markdown-it-footnote": "^3.0.2",
+ "markdown-it-ins": "^3.0.0",
+ "markdown-it-mark": "^3.0.0",
+ "markdown-it-multimd-table": "^4.0.0",
+ "markdown-it-sub": "^1.0.0",
+ "markdown-it-sup": "^1.0.0",
+ "markdown-it-toc-done-right": "^4.1.0",
+ "md5": "^2.2.1",
+ "uslug": "^1.0.4"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+ "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
+ },
+ "markdown-it": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
+ "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
+ "requires": {
+ "argparse": "^1.0.7",
+ "entities": "~2.0.0",
+ "linkify-it": "^2.0.0",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ }
+ }
+ },
"joplin-turndown": {
"version": "4.0.19",
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.19.tgz",
@@ -4547,37 +4605,19 @@
"integrity": "sha512-JVW6fCmZWjvMdDQSbOT3nnOQtd9iAXmw7hTSh26+v42BnvXeVyGMDBm5b/EZocMed2MbCAHiTX632vY0FyGB8A=="
},
"markdown-it-ins": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-2.0.0.tgz",
- "integrity": "sha1-papqMPHi9x6Ul1Z8/f9A8f3mdIM="
- },
- "markdown-it-katex": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/markdown-it-katex/-/markdown-it-katex-2.0.3.tgz",
- "integrity": "sha1-17hqGuoLnWSW+rTnkZoY/e9YnDk=",
- "requires": {
- "katex": "^0.6.0"
- },
- "dependencies": {
- "katex": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/katex/-/katex-0.6.0.tgz",
- "integrity": "sha1-EkGOCRIcBckgQbazuftrqyE8tvM=",
- "requires": {
- "match-at": "^0.1.0"
- }
- }
- }
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-3.0.0.tgz",
+ "integrity": "sha512-+vyAdBuMGwmT2yMlAFJSx2VR/0QZ1onQ/Mkkmr4l9tDFOh5sVoAgRbkgbuSsk+sxJ9vaMH/IQ323ydfvQrPO/Q=="
},
"markdown-it-mark": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-2.0.0.tgz",
- "integrity": "sha1-RqGqlHEFrtgYiXjgoBYXnkBPQsc="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-3.0.0.tgz",
+ "integrity": "sha512-HqMWeKfMMOu4zBO0emmxsoMWmbf2cPKZY1wP6FsTbKmicFfp5y4L3KXAsNeO1rM6NTJVOrNlLKMPjWzriBGspw=="
},
"markdown-it-multimd-table": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/markdown-it-multimd-table/-/markdown-it-multimd-table-3.2.0.tgz",
- "integrity": "sha512-dBtYHVtyAnoWslzYLYDh0d9jX2/qroyMIsJ1BjFdIcLgZjzqBIBGwZ1j3AaaWh+IIfe3KVm5Irqn5Uzadm5kQA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-multimd-table/-/markdown-it-multimd-table-4.0.0.tgz",
+ "integrity": "sha512-kdM3fH+/sRMfHQgD2CM1BcIpLNODUCuoiFr6TwS7mDJBYntVXDJxZbFwGDRflIc9ZzAfsUbr0lnHc6RbYafIsw==",
"requires": {
"markdown-it": "^8.4.2"
},
@@ -4611,11 +4651,6 @@
"resolved": "https://registry.npmjs.org/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.1.0.tgz",
"integrity": "sha512-UhD2Oj6cZV3ycYPoelt4hTkwKIK3zbPP1wjjdpCq7UGtWQOFalDFDv1s2zBYV6aR2gMs/X8kpJcOYsQmUbiXDw=="
},
- "match-at": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/match-at/-/match-at-0.1.1.tgz",
- "integrity": "sha512-h4Yd392z9mST+dzc+yjuybOGFNOZjmXIPKWjxBd1Bb23r4SmDOsk2NYCU2BMUBGbSpZqwVsZYNq26QS3xfaT3Q=="
- },
"matcher": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz",
@@ -4826,6 +4861,11 @@
}
}
},
+ "neo-async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
+ },
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
@@ -5112,7 +5152,6 @@
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
- "optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
@@ -5231,6 +5270,15 @@
"mimic-fn": "^1.0.0"
}
},
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ }
+ },
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -5422,8 +5470,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
- "dev": true,
- "optional": true
+ "dev": true
},
"is-glob": {
"version": "2.0.1",
@@ -5993,15 +6040,13 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
- "dev": true,
- "optional": true
+ "dev": true
},
"repeat-element": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
- "dev": true,
- "optional": true
+ "dev": true
},
"repeat-string": {
"version": "1.6.1",
@@ -7149,6 +7194,11 @@
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
},
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ },
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
diff --git a/ElectronClient/app/package.json b/ElectronClient/app/package.json
index 0ee0daf03..78742dbe5 100644
--- a/ElectronClient/app/package.json
+++ b/ElectronClient/app/package.json
@@ -99,34 +99,20 @@
"es6-promise-pool": "^2.5.0",
"file-uri-to-path": "^1.0.0",
"follow-redirects": "^1.5.0",
- "font-awesome-filetypes": "^2.1.0",
"form-data": "^2.3.2",
"formatcoords": "^1.1.3",
"fs-extra": "^5.0.0",
- "highlight.js": "^9.15.6",
"html-entities": "^1.2.1",
"html-minifier": "^4.0.0",
"image-type": "^3.0.0",
+ "joplin-renderer": "^1.0.4",
"joplin-turndown": "^4.0.19",
"joplin-turndown-plugin-gfm": "^1.0.11",
"jssha": "^2.3.1",
- "katex": "^0.11.1",
"levenshtein": "^1.0.5",
"lodash": "^4.17.15",
"mark.js": "^8.11.1",
"markdown-it": "^8.4.1",
- "markdown-it-abbr": "^1.0.4",
- "markdown-it-anchor": "^5.2.5",
- "markdown-it-deflist": "^2.0.3",
- "markdown-it-emoji": "^1.4.0",
- "markdown-it-footnote": "^3.0.2",
- "markdown-it-ins": "^2.0.0",
- "markdown-it-katex": "^2.0.3",
- "markdown-it-mark": "^2.0.0",
- "markdown-it-multimd-table": "^3.2.0",
- "markdown-it-sub": "^1.0.0",
- "markdown-it-sup": "^1.0.0",
- "markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"moment": "^2.22.2",
"multiparty": "^4.2.1",
@@ -157,7 +143,6 @@
"tcp-port-used": "^0.1.2",
"uglifycss": "0.0.29",
"url-parse": "^1.4.3",
- "uslug": "^1.0.4",
"uuid": "^3.2.1",
"valid-url": "^1.0.9",
"xml2js": "^0.4.19"
diff --git a/ElectronClient/run.bat b/ElectronClient/run.bat
index c7ad48273..4640da608 100644
--- a/ElectronClient/run.bat
+++ b/ElectronClient/run.bat
@@ -5,5 +5,7 @@ set script_dir=%mypath:~0,-1%
call build.bat
if %errorlevel% neq 0 exit /b %errorlevel%
+rem xcopy /C /I /H /R /Y /S /Q D:\Docs\PROGS\Node\joplin-renderer %script_dir%\app\node_modules\joplin-renderer
+
cd %script_dir%\app
call .\node_modules\.bin\electron.cmd . --env dev --log-level debug --no-welcome --open-dev-tools "$@"
diff --git a/ReactNativeClient/PluginAssetsLoader.ts b/ReactNativeClient/PluginAssetsLoader.ts
new file mode 100644
index 000000000..0124baf84
--- /dev/null
+++ b/ReactNativeClient/PluginAssetsLoader.ts
@@ -0,0 +1,55 @@
+const { dirname } = require('lib/path-utils.js');
+const { shim } = require('lib/shim');
+const Setting = require('lib/models/Setting');
+const pluginAssets = require('./pluginAssets/index');
+const KvStore = require('lib/services/KvStore.js');
+
+export default class PluginAssetsLoader {
+
+ static instance_:PluginAssetsLoader = null;
+ logger_:any = null;
+
+ static instance() {
+ if (PluginAssetsLoader.instance_) return PluginAssetsLoader.instance_;
+ PluginAssetsLoader.instance_ = new PluginAssetsLoader();
+ return PluginAssetsLoader.instance_;
+ }
+
+ setLogger(logger:any) {
+ this.logger_ = logger;
+ }
+
+ logger() {
+ return this.logger_;
+ }
+
+ async importAssets() {
+ const destDir = `${Setting.value('resourceDir')}/pluginAssets`;
+ await shim.fsDriver().mkdir(destDir);
+
+ const hash = pluginAssets.hash;
+ if (hash === await KvStore.instance().value('PluginAssetsLoader.lastHash')) {
+ this.logger().info(`PluginAssetsLoader: Assets are up to date. Hash: ${hash}`);
+ return;
+ }
+
+ this.logger().info(`PluginAssetsLoader: Importing assets to ${destDir}`);
+
+ try {
+ for (let name in pluginAssets.files) {
+ const dataBase64 = pluginAssets.files[name].data;
+ const destPath = `${destDir}/${name}`;
+ await shim.fsDriver().mkdir(dirname(destPath));
+ await shim.fsDriver().unlink(destPath);
+
+ this.logger().info(`PluginAssetsLoader: Copying: ${name} => ${destPath}`);
+ await shim.fsDriver().writeFile(destPath, dataBase64);
+ }
+ } catch (error) {
+ this.logger().error(error);
+ }
+
+ KvStore.instance().setValue('PluginAssetsLoader.lastHash', hash);
+ }
+
+}
diff --git a/ReactNativeClient/encodeAssets.js b/ReactNativeClient/encodeAssets.js
new file mode 100644
index 000000000..86d986b0c
--- /dev/null
+++ b/ReactNativeClient/encodeAssets.js
@@ -0,0 +1,74 @@
+require('app-module-path').addPath(`${__dirname}`);
+
+const fs = require('fs-extra');
+const { dirname, fileExtension } = require('lib/path-utils');
+const md5 = require('md5');
+
+const rootDir = __dirname;
+const outputDir = `${rootDir}/pluginAssets`;
+
+var walk = function(dir) {
+ var results = [];
+ var list = fs.readdirSync(dir);
+ list.forEach(function(file) {
+ file = `${dir}/${file}`;
+ var stat = fs.statSync(file);
+ if (stat && stat.isDirectory()) {
+ results = results.concat(walk(file));
+ } else {
+ results.push(file);
+ }
+ });
+ return results;
+};
+
+async function encodeFile(sourcePath, destPath) {
+ const buffer = await fs.readFile(sourcePath);
+ const hash = md5(buffer.toString('base64'));
+ const js = `module.exports = \`${buffer.toString('base64')}\`;`;
+ const outputPath = `${outputDir}/${destPath}.base64.js`;
+ await fs.mkdirp(dirname(outputPath));
+ await fs.writeFile(outputPath, js);
+
+ const ext = fileExtension(sourcePath).toLowerCase();
+ let mime = 'application/octet-stream';
+ if (ext === 'js') mime = 'application/javascript';
+ if (ext === 'css') mime = 'text/css';
+
+ return {
+ encoding: 'base64',
+ name: destPath,
+ encodedName: `${destPath}.base64.js`,
+ mime: mime,
+ hash: hash,
+ };
+}
+
+async function main() {
+ await fs.mkdirp(outputDir);
+
+ const encodedFiles = [];
+ const sourceAssetDir = `${rootDir}/node_modules/joplin-renderer/assets`;
+ const files = walk(sourceAssetDir);
+
+ for (const file of files) {
+ const destFile = file.substr(sourceAssetDir.length + 1);
+ encodedFiles.push(await encodeFile(file, destFile));
+ }
+
+ const hashes = [];
+ const indexJs = [];
+ for (const file of encodedFiles) {
+ indexJs.push(`'${file.name}': { data: require('./${file.encodedName}'), mime: '${file.mime}', encoding: '${file.encoding}' },`);
+ hashes.push(file.hash);
+ }
+
+ const hash = md5(hashes.join(''));
+
+ await fs.writeFile(`${outputDir}/index.js`, `module.exports = {\nhash:"${hash}", files: {\n${indexJs.join('\n')}\n}\n};`);
+}
+
+main().catch((error) => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/ReactNativeClient/lib/components/global-style.js b/ReactNativeClient/lib/components/global-style.js
index 163fb17a9..86902e2ea 100644
--- a/ReactNativeClient/lib/components/global-style.js
+++ b/ReactNativeClient/lib/components/global-style.js
@@ -38,7 +38,7 @@ const globalStyle = {
htmlCodeBorderColor: 'rgb(220, 220, 220)',
htmlCodeColor: 'rgb(0,0,0)',
- codeThemeCss: 'hljs-atom-one-light.css',
+ codeThemeCss: 'atom-one-light.css',
};
globalStyle.marginRight = globalStyle.margin;
diff --git a/ReactNativeClient/lib/components/note-body-viewer.js b/ReactNativeClient/lib/components/note-body-viewer.js
index 40a72b9fc..f83e30b1e 100644
--- a/ReactNativeClient/lib/components/note-body-viewer.js
+++ b/ReactNativeClient/lib/components/note-body-viewer.js
@@ -1,13 +1,15 @@
const React = require('react');
const Component = React.Component;
-const { Platform, View } = require('react-native');
+const { Platform, View, Text } = require('react-native');
const { WebView } = require('react-native-webview');
const { themeStyle } = require('lib/components/global-style.js');
const Setting = require('lib/models/Setting.js');
const { reg } = require('lib/registry.js');
const { shim } = require('lib/shim');
-const MdToHtml = require('lib/renderers/MdToHtml.js');
const shared = require('lib/components/shared/note-screen-shared.js');
+const markupLanguageUtils = require('lib/markupLanguageUtils');
+
+import Async from 'react-async';
class NoteBodyViewer extends Component {
constructor() {
@@ -15,21 +17,134 @@ class NoteBodyViewer extends Component {
this.state = {
resources: {},
webViewLoaded: false,
+ bodyHtml: '',
};
this.isMounted_ = false;
+
+ this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
+
+ this.reloadNote = this.reloadNote.bind(this);
}
- UNSAFE_componentWillMount() {
- this.mdToHtml_ = new MdToHtml();
+ componentDidMount() {
this.isMounted_ = true;
}
componentWillUnmount() {
- this.mdToHtml_ = null;
+ this.markupToHtml_ = null;
this.isMounted_ = false;
}
+ async reloadNote() {
+ const note = this.props.note;
+ const theme = themeStyle(this.props.theme);
+
+ const bodyToRender = note ? note.body : '';
+
+ const mdOptions = {
+ onResourceLoaded: () => {
+ if (this.resourceLoadedTimeoutId_) {
+ clearTimeout(this.resourceLoadedTimeoutId_);
+ this.resourceLoadedTimeoutId_ = null;
+ }
+
+ this.resourceLoadedTimeoutId_ = setTimeout(() => {
+ this.resourceLoadedTimeoutId_ = null;
+ this.forceUpdate();
+ }, 100);
+ },
+ paddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
+ highlightedKeywords: this.props.highlightedKeywords,
+ resources: this.props.noteResources, // await shared.attachedResources(bodyToRender),
+ codeTheme: theme.codeThemeCss,
+ postMessageSyntax: 'window.ReactNativeWebView.postMessage',
+ };
+
+ let result = await this.markupToHtml_.render(note.markup_language, bodyToRender, this.props.webViewStyle, mdOptions);
+ let html = result.html;
+
+ const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
+
+ const injectedJs = [];
+ injectedJs.push(shim.injectedJs('webviewLib'));
+ injectedJs.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
+ injectedJs.push(`
+ const readyStateCheckInterval = setInterval(function() {
+ if (document.readyState === "complete") {
+ clearInterval(readyStateCheckInterval);
+ if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
+
+ const hash = "${this.props.noteHash}";
+ // Gives it a bit of time before scrolling to the anchor
+ // so that images are loaded.
+ if (hash) {
+ setTimeout(() => {
+ const e = document.getElementById(hash);
+ if (!e) {
+ console.warn('Cannot find hash', hash);
+ return;
+ }
+ e.scrollIntoView();
+ }, 500);
+ }
+ }
+ }, 10);
+ `);
+
+ const headers = [];
+ for (let i = 0; i < result.pluginAssets.length; i++) {
+ const asset = result.pluginAssets[i];
+ if (asset.mime === 'text/css') {
+ headers.push(` `);
+ } else if (asset.mime === 'application/javascript') {
+ // NOT TESTED!!
+ headers.push(``);
+ }
+ }
+
+ html =
+ `
+
+
+
+
+ ${headers.join('\n')}
+
+
+ ${html}
+
+
+ `;
+
+ // On iOS scalesPageToFit work like this:
+ //
+ // Find the widest image, resize it *and everything else* by x% so that
+ // the image fits within the viewport. The problem is that it means if there's
+ // a large image, everything is going to be scaled to a very small size, making
+ // the text unreadable.
+ //
+ // On Android:
+ //
+ // Find the widest elements and scale them (and them only) to fit within the viewport
+ // It means it's going to scale large images, but the text will remain at the normal
+ // size.
+ //
+ // That means we can use scalesPageToFix on Android but not on iOS.
+ // The weird thing is that on iOS, scalesPageToFix=false along with a CSS
+ // rule "img { max-width: 100% }", works like scalesPageToFix=true on Android.
+ // So we use scalesPageToFix=false on iOS along with that CSS rule.
+
+ // `baseUrl` is where the images will be loaded from. So images must use a path relative to resourceDir.
+ return {
+ source: {
+ html: html,
+ baseUrl: `file://${Setting.value('resourceDir')}/`,
+ },
+ injectedJs: injectedJs,
+ };
+ }
+
onLoadEnd() {
setTimeout(() => {
if (this.props.onLoadEnd) this.props.onLoadEnd();
@@ -70,80 +185,18 @@ class NoteBodyViewer extends Component {
}
rebuildMd() {
- // this.mdToHtml_.clearCache();
this.forceUpdate();
}
render() {
- const note = this.props.note;
- const style = this.props.style;
+ // Note: useWebKit={false} is needed to go around this bug:
+ // https://github.com/react-native-community/react-native-webview/issues/376
+ // However, if we add the tag as described there, it is no longer necessary and WebKit can be used!
+ // https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-501991406
+ //
+ // However, on iOS, due to the bug below, we cannot use WebKit:
+ // https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-503754654
- const theme = themeStyle(this.props.theme);
-
- const bodyToRender = note ? note.body : '';
-
- const mdOptions = {
- onResourceLoaded: () => {
- if (this.resourceLoadedTimeoutId_) {
- clearTimeout(this.resourceLoadedTimeoutId_);
- this.resourceLoadedTimeoutId_ = null;
- }
-
- this.resourceLoadedTimeoutId_ = setTimeout(() => {
- this.resourceLoadedTimeoutId_ = null;
- this.forceUpdate();
- }, 100);
- },
- paddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
- highlightedKeywords: this.props.highlightedKeywords,
- resources: this.props.noteResources, // await shared.attachedResources(bodyToRender),
- codeTheme: theme.codeThemeCss,
- postMessageSyntax: 'window.ReactNativeWebView.postMessage',
- };
-
- let result = this.mdToHtml_.render(bodyToRender, this.props.webViewStyle, mdOptions);
- let html = result.html;
-
- const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
-
- const injectedJs = [this.mdToHtml_.injectedJavaScript()];
- injectedJs.push(shim.injectedJs('webviewLib'));
- injectedJs.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
- injectedJs.push(`
- const readyStateCheckInterval = setInterval(function() {
- if (document.readyState === "complete") {
- clearInterval(readyStateCheckInterval);
- if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
-
- const hash = "${this.props.noteHash}";
- // Gives it a bit of time before scrolling to the anchor
- // so that images are loaded.
- if (hash) {
- setTimeout(() => {
- const e = document.getElementById(hash);
- if (!e) {
- console.warn('Cannot find hash', hash);
- return;
- }
- e.scrollIntoView();
- }, 500);
- }
- }
- }, 10);
- `);
-
- html =
- `
-
-
-
-
-
-
- ${html}
-
-
- `;
let webViewStyle = { backgroundColor: this.props.webViewStyle.backgroundColor };
// On iOS, the onLoadEnd() event is never fired so always
@@ -153,68 +206,49 @@ class NoteBodyViewer extends Component {
webViewStyle.opacity = this.state.webViewLoaded ? 1 : 0.01;
}
- // On iOS scalesPageToFit work like this:
- //
- // Find the widest image, resize it *and everything else* by x% so that
- // the image fits within the viewport. The problem is that it means if there's
- // a large image, everything is going to be scaled to a very small size, making
- // the text unreadable.
- //
- // On Android:
- //
- // Find the widest elements and scale them (and them only) to fit within the viewport
- // It means it's going to scale large images, but the text will remain at the normal
- // size.
- //
- // That means we can use scalesPageToFix on Android but not on iOS.
- // The weird thing is that on iOS, scalesPageToFix=false along with a CSS
- // rule "img { max-width: 100% }", works like scalesPageToFix=true on Android.
- // So we use scalesPageToFix=false on iOS along with that CSS rule.
-
- // `baseUrl` is where the images will be loaded from. So images must use a path relative to resourceDir.
- const source = {
- html: html,
- baseUrl: `file://${Setting.value('resourceDir')}/`,
- };
-
- // Note: useWebKit={false} is needed to go around this bug:
- // https://github.com/react-native-community/react-native-webview/issues/376
- // However, if we add the tag as described there, it is no longer necessary and WebKit can be used!
- // https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-501991406
- //
- // However, on iOS, due to the bug below, we cannot use WebKit:
- // https://github.com/react-native-community/react-native-webview/issues/312#issuecomment-503754654
-
return (
-
- this.onLoadEnd()}
- onError={() => reg.logger().error('WebView error')}
- onMessage={event => {
- // Since RN 58 (or 59) messages are now escaped twice???
- let msg = unescape(unescape(event.nativeEvent.data));
-
- console.info('Got IPC message: ', msg);
-
- if (msg.indexOf('checkboxclick:') === 0) {
- const newBody = shared.toggleCheckbox(msg, this.props.note.body);
- if (this.props.onCheckboxChange) this.props.onCheckboxChange(newBody);
- } else if (msg.indexOf('markForDownload:') === 0) {
- msg = msg.split(':');
- const resourceId = msg[1];
- if (this.props.onMarkForDownload) this.props.onMarkForDownload({ resourceId: resourceId });
- } else {
- this.props.onJoplinLinkClick(msg);
+
+
+ {({ data, error, isPending }) => {
+ if (error) {
+ console.error(error);
+ return {error.message} ;
}
+
+ if (isPending) return null;
+
+ return (
+ this.onLoadEnd()}
+ onError={() => reg.logger().error('WebView error')}
+ onMessage={event => {
+ // Since RN 58 (or 59) messages are now escaped twice???
+ let msg = unescape(unescape(event.nativeEvent.data));
+
+ console.info('Got IPC message: ', msg);
+
+ if (msg.indexOf('checkboxclick:') === 0) {
+ const newBody = shared.toggleCheckbox(msg, this.props.note.body);
+ if (this.props.onCheckboxChange) this.props.onCheckboxChange(newBody);
+ } else if (msg.indexOf('markForDownload:') === 0) {
+ msg = msg.split(':');
+ const resourceId = msg[1];
+ if (this.props.onMarkForDownload) this.props.onMarkForDownload({ resourceId: resourceId });
+ } else {
+ this.props.onJoplinLinkClick(msg);
+ }
+ }}
+ />
+ );
}}
- />
+
);
}
diff --git a/ReactNativeClient/lib/fs-driver-rn.js b/ReactNativeClient/lib/fs-driver-rn.js
index f87f60ea5..6363219a2 100644
--- a/ReactNativeClient/lib/fs-driver-rn.js
+++ b/ReactNativeClient/lib/fs-driver-rn.js
@@ -38,7 +38,13 @@ class FsDriverRN extends FsDriverBase {
if (!options) options = {};
if (!('recursive' in options)) options.recursive = false;
- let items = await RNFS.readDir(path);
+ let items = [];
+ try {
+ items = await RNFS.readDir(path);
+ } catch (error) {
+ throw new Error(`Could not read directory: ${path}: ${error.message}`);
+ }
+
let output = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
diff --git a/ReactNativeClient/lib/import-enex.js b/ReactNativeClient/lib/import-enex.js
index 09a575821..a25155fc1 100644
--- a/ReactNativeClient/lib/import-enex.js
+++ b/ReactNativeClient/lib/import-enex.js
@@ -4,6 +4,7 @@ const BaseModel = require('lib/BaseModel.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const Resource = require('lib/models/Resource.js');
+const { MarkupToHtml } = require('joplin-renderer');
const { enexXmlToMd } = require('./import-enex-md-gen.js');
const { enexXmlToHtml } = require('./import-enex-html-gen.js');
const { time } = require('lib/time-utils.js');
@@ -224,8 +225,8 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
delete note.bodyXml;
note.markup_language = importOptions.outputFormat === 'html' ?
- Note.MARKUP_LANGUAGE_HTML :
- Note.MARKUP_LANGUAGE_MARKDOWN;
+ MarkupToHtml.MARKUP_LANGUAGE_HTML :
+ MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
// console.info('*************************************************************************');
// console.info(body);
diff --git a/ReactNativeClient/lib/joplin-database.js b/ReactNativeClient/lib/joplin-database.js
index c685dbd63..8b3ec7669 100644
--- a/ReactNativeClient/lib/joplin-database.js
+++ b/ReactNativeClient/lib/joplin-database.js
@@ -287,6 +287,11 @@ class JoplinDatabase extends Database {
});
}
+ addMigrationFile(num) {
+ const timestamp = Date.now();
+ return { sql: 'INSERT INTO migrations (number, created_time, updated_time) VALUES (?, ?, ?)', params: [num, timestamp, timestamp] };
+ }
+
async upgradeDatabase(fromVersion) {
// INSTRUCTIONS TO UPGRADE THE DATABASE:
//
@@ -302,7 +307,7 @@ class JoplinDatabase extends Database {
// must be set in the synchronizer too.
// Note: v16 and v17 don't do anything. They were used to debug an issue.
- const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26];
+ const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
@@ -596,9 +601,8 @@ class JoplinDatabase extends Database {
`;
queries.push(this.sqlStringToLines(newTableSql)[0]);
- const timestamp = Date.now();
queries.push('ALTER TABLE resources ADD COLUMN `size` INT NOT NULL DEFAULT -1');
- queries.push({ sql: 'INSERT INTO migrations (number, created_time, updated_time) VALUES (20, ?, ?)', params: [timestamp, timestamp] });
+ queries.push(this.addMigrationFile(20));
}
if (targetVersion == 21) {
@@ -657,6 +661,10 @@ class JoplinDatabase extends Database {
}
}
+ if (targetVersion == 27) {
+ queries.push(this.addMigrationFile(27));
+ }
+
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
try {
diff --git a/ReactNativeClient/lib/markdownUtils.js b/ReactNativeClient/lib/markdownUtils.js
index 51e155ce4..c619b54db 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/renderers/MdToHtml/setupLinkify');
+const { setupLinkify } = require('joplin-renderer');
const markdownUtils = {
// Not really escaping because that's not supported by marked.js
diff --git a/ReactNativeClient/lib/markupLanguageUtils.js b/ReactNativeClient/lib/markupLanguageUtils.js
index 5e0de8b78..50b863597 100644
--- a/ReactNativeClient/lib/markupLanguageUtils.js
+++ b/ReactNativeClient/lib/markupLanguageUtils.js
@@ -1,17 +1,36 @@
const markdownUtils = require('lib/markdownUtils');
const htmlUtils = require('lib/htmlUtils');
-const Note = require('lib/models/Note');
+const Setting = require('lib/models/Setting');
+const Resource = require('lib/models/Resource');
+const { MarkupToHtml } = require('joplin-renderer');
class MarkupLanguageUtils {
lib_(language) {
- if (language === Note.MARKUP_LANGUAGE_HTML) return htmlUtils;
- if (language === Note.MARKUP_LANGUAGE_MARKDOWN) return markdownUtils;
+ if (language === MarkupToHtml.MARKUP_LANGUAGE_HTML) return htmlUtils;
+ if (language === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) return markdownUtils;
throw new Error(`Unsupported markup language: ${language}`);
}
extractImageUrls(language, text) {
return this.lib_(language).extractImageUrls(text);
}
+
+ // Create a new MarkupToHtml instance while injecting options specific to Joplin
+ // desktop and mobile applications.
+ newMarkupToHtml(options = null) {
+ const subValues = Setting.subValues('markdown.plugin', Setting.toPlainObject());
+ const pluginOptions = {};
+ for (const n in subValues) {
+ pluginOptions[n] = { enabled: subValues[n] };
+ }
+
+ options = Object.assign({
+ ResourceModel: Resource,
+ pluginOptions: pluginOptions,
+ }, options);
+
+ return new MarkupToHtml(options);
+ }
}
const markupLanguageUtils = new MarkupLanguageUtils();
diff --git a/ReactNativeClient/lib/migrations/27.js b/ReactNativeClient/lib/migrations/27.js
new file mode 100644
index 000000000..c861c06bd
--- /dev/null
+++ b/ReactNativeClient/lib/migrations/27.js
@@ -0,0 +1,10 @@
+const Setting = require('lib/models/Setting');
+
+const script = {};
+
+script.exec = async function() {
+ Setting.setValue('markdown.plugin.softbreaks', Setting.value('markdown.softbreaks'));
+ Setting.setValue('markdown.plugin.typographer', Setting.value('markdown.typographer'));
+};
+
+module.exports = script;
diff --git a/ReactNativeClient/lib/models/Migration.js b/ReactNativeClient/lib/models/Migration.js
index 08c2b072e..8e7964c45 100644
--- a/ReactNativeClient/lib/models/Migration.js
+++ b/ReactNativeClient/lib/models/Migration.js
@@ -2,6 +2,7 @@ const BaseModel = require('lib/BaseModel.js');
const migrationScripts = {
20: require('lib/migrations/20.js'),
+ 27: require('lib/migrations/27.js'),
};
class Migration extends BaseModel {
@@ -18,6 +19,7 @@ class Migration extends BaseModel {
}
static script(number) {
+ if (!migrationScripts[number]) throw new Error('Migration script has not been added to "migrationScripts" array');
return migrationScripts[number];
}
}
diff --git a/ReactNativeClient/lib/models/Note.js b/ReactNativeClient/lib/models/Note.js
index 8239da439..93c65b205 100644
--- a/ReactNativeClient/lib/models/Note.js
+++ b/ReactNativeClient/lib/models/Note.js
@@ -11,6 +11,7 @@ const { _ } = require('lib/locale.js');
const ArrayUtils = require('lib/ArrayUtils.js');
const lodash = require('lodash');
const urlUtils = require('lib/urlUtils.js');
+const { MarkupToHtml } = require('joplin-renderer');
class Note extends BaseItem {
static tableName() {
@@ -624,8 +625,8 @@ class Note extends BaseItem {
}
static markupLanguageToLabel(markupLanguageId) {
- if (markupLanguageId === Note.MARKUP_LANGUAGE_MARKDOWN) return 'Markdown';
- if (markupLanguageId === Note.MARKUP_LANGUAGE_HTML) return 'HTML';
+ if (markupLanguageId === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) return 'Markdown';
+ if (markupLanguageId === MarkupToHtml.MARKUP_LANGUAGE_HTML) return 'HTML';
throw new Error(`Invalid markup language ID: ${markupLanguageId}`);
}
}
@@ -633,7 +634,4 @@ class Note extends BaseItem {
Note.updateGeolocationEnabled_ = true;
Note.geolocationUpdating_ = false;
-Note.MARKUP_LANGUAGE_MARKDOWN = 1;
-Note.MARKUP_LANGUAGE_HTML = 2;
-
module.exports = Note;
diff --git a/ReactNativeClient/lib/models/Setting.js b/ReactNativeClient/lib/models/Setting.js
index d2a8efd2d..ec1fea757 100644
--- a/ReactNativeClient/lib/models/Setting.js
+++ b/ReactNativeClient/lib/models/Setting.js
@@ -334,8 +334,14 @@ class Setting extends BaseModel {
};
},
},
- 'markdown.softbreaks': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable soft breaks') },
- 'markdown.typographer': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable typographer support') },
+
+ // Deprecated - use markdown.plugin.*
+ 'markdown.softbreaks': { value: false, type: Setting.TYPE_BOOL, public: false, appTypes: ['mobile', 'desktop']},
+ 'markdown.typographer': { value: false, type: Setting.TYPE_BOOL, public: false, appTypes: ['mobile', 'desktop']},
+ // Deprecated
+
+ 'markdown.plugin.softbreaks': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable soft breaks') },
+ 'markdown.plugin.typographer': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable typographer support') },
'markdown.plugin.katex': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable math expressions') },
'markdown.plugin.mark': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable ==mark== syntax') },
'markdown.plugin.footnote': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable footnotes') },
@@ -848,12 +854,14 @@ class Setting extends BaseModel {
// { sync.5.path: 'http://example', sync.5.username: 'testing' }
// and baseKey is 'sync.5', the function will return
// { path: 'http://example', username: 'testing' }
- static subValues(baseKey, settings) {
+ static subValues(baseKey, settings, options = null) {
+ const includeBaseKeyInName = !!options && !!options.includeBaseKeyInName;
+
let output = {};
for (let key in settings) {
if (!settings.hasOwnProperty(key)) continue;
if (key.indexOf(baseKey) === 0) {
- const subKey = key.substr(baseKey.length + 1);
+ const subKey = includeBaseKeyInName ? key : key.substr(baseKey.length + 1);
output[subKey] = settings[key];
}
}
diff --git a/ReactNativeClient/lib/renderers/HtmlToHtml.js b/ReactNativeClient/lib/renderers/HtmlToHtml.js
deleted file mode 100644
index ab719ea26..000000000
--- a/ReactNativeClient/lib/renderers/HtmlToHtml.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const htmlUtils = require('lib/htmlUtils');
-const utils = require('./utils');
-const noteStyle = require('./noteStyle');
-
-class HtmlToHtml {
- constructor(options) {
- if (!options) options = {};
- this.resourceBaseUrl_ = 'resourceBaseUrl' in options ? options.resourceBaseUrl : null;
- }
-
- render(markup, theme, options) {
- const html = htmlUtils.processImageTags(markup, data => {
- if (!data.src) return null;
-
- const r = utils.imageReplacement(data.src, options.resources, this.resourceBaseUrl_);
- if (!r) return null;
-
- if (typeof r === 'string') {
- return {
- type: 'replaceElement',
- html: r,
- };
- } else {
- return {
- type: 'setAttributes',
- attrs: r,
- };
- }
- });
-
- const cssStrings = noteStyle(theme, options);
- const styleHtml = ``;
-
- return {
- html: styleHtml + html,
- cssFiles: [],
- };
- }
-}
-
-module.exports = HtmlToHtml;
diff --git a/ReactNativeClient/lib/renderers/MarkupToHtml.js b/ReactNativeClient/lib/renderers/MarkupToHtml.js
deleted file mode 100644
index 57478e213..000000000
--- a/ReactNativeClient/lib/renderers/MarkupToHtml.js
+++ /dev/null
@@ -1,37 +0,0 @@
-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];
- }
-
- injectedJavaScript() {
- return '';
- }
-
- render(markupLanguage, markup, theme, options) {
- return this.renderer(markupLanguage).render(markup, theme, options);
- }
-}
-
-module.exports = MarkupToHtml;
diff --git a/ReactNativeClient/lib/renderers/MdToHtml.js b/ReactNativeClient/lib/renderers/MdToHtml.js
deleted file mode 100644
index 659e02dfb..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml.js
+++ /dev/null
@@ -1,199 +0,0 @@
-const MarkdownIt = require('markdown-it');
-const { shim } = require('lib/shim.js');
-const md5 = require('md5');
-const noteStyle = require('./noteStyle');
-const Setting = require('lib/models/Setting.js');
-const rules = {
- image: require('./MdToHtml/rules/image'),
- checkbox: require('./MdToHtml/rules/checkbox'),
- katex: require('./MdToHtml/rules/katex'),
- link_open: require('./MdToHtml/rules/link_open'),
- html_image: require('./MdToHtml/rules/html_image'),
- highlight_keywords: require('./MdToHtml/rules/highlight_keywords'),
- code_inline: require('./MdToHtml/rules/code_inline'),
- fountain: require('./MdToHtml/rules/fountain'),
-};
-const setupLinkify = require('./MdToHtml/setupLinkify');
-const hljs = require('highlight.js');
-const uslug = require('uslug');
-const markdownItAnchor = require('markdown-it-anchor');
-// The keys must match the corresponding entry in Setting.js
-const plugins = {
- mark: { module: require('markdown-it-mark') },
- footnote: { module: require('markdown-it-footnote') },
- sub: { module: require('markdown-it-sub') },
- sup: { module: require('markdown-it-sup') },
- deflist: { module: require('markdown-it-deflist') },
- abbr: { module: require('markdown-it-abbr') },
- emoji: { module: require('markdown-it-emoji') },
- insert: { module: require('markdown-it-ins') },
- multitable: { module: require('markdown-it-multimd-table'), options: { enableMultilineRows: true, enableRowspan: true } },
- toc: { module: require('markdown-it-toc-done-right'), options: { listType: 'ul', slugify: uslugify } },
-};
-
-function uslugify(s) {
- return uslug(s);
-}
-
-class MdToHtml {
- constructor(options = null) {
- if (!options) options = {};
-
- // Must include last "/"
- this.resourceBaseUrl_ = 'resourceBaseUrl' in options ? options.resourceBaseUrl : null;
-
- this.cachedOutputs_ = {};
-
- this.lastCodeHighlightCacheKey_ = null;
- this.cachedHighlightedCode_ = {};
- }
-
- render(body, style, options = null) {
- if (!options) options = {};
- if (!options.postMessageSyntax) options.postMessageSyntax = 'postMessage';
- if (!options.paddingBottom) options.paddingBottom = '0';
- if (!options.highlightedKeywords) options.highlightedKeywords = [];
-
- // The "codeHighlightCacheKey" option indicates what set of cached object should be
- // associated with this particular Markdown body. It is only used to allow us to
- // clear the cache whenever switching to a different note.
- // If "codeHighlightCacheKey" is not specified, code highlighting won't be cached.
- if (options.codeHighlightCacheKey !== this.lastCodeHighlightCacheKey_ || !options.codeHighlightCacheKey) {
- this.cachedHighlightedCode_ = {};
- this.lastCodeHighlightCacheKey_ = options.codeHighlightCacheKey;
- }
-
- const breaks_ = Setting.value('markdown.softbreaks') ? false : true;
- const typographer_ = Setting.value('markdown.typographer') ? true : false;
-
- const cacheKey = md5(escape(body + JSON.stringify(options) + JSON.stringify(style)));
- const cachedOutput = this.cachedOutputs_[cacheKey];
- if (cachedOutput) return cachedOutput;
-
- const context = {
- css: {},
- cssFiles: {},
- assetLoaders: {},
- };
-
- const ruleOptions = Object.assign({}, options, { resourceBaseUrl: this.resourceBaseUrl_ });
-
- const markdownIt = new MarkdownIt({
- breaks: breaks_,
- typographer: typographer_,
- linkify: true,
- html: true,
- highlight: (str, lang) => {
- try {
- let hlCode = '';
-
- const cacheKey = md5(`${str}_${lang}`);
-
- if (options.codeHighlightCacheKey && this.cachedHighlightedCode_[cacheKey]) {
- hlCode = this.cachedHighlightedCode_[cacheKey];
- } else {
- if (lang && hljs.getLanguage(lang)) {
- hlCode = hljs.highlight(lang, str, true).value;
- } else {
- hlCode = hljs.highlightAuto(str).value;
- }
- this.cachedHighlightedCode_[cacheKey] = hlCode;
- }
-
- if (shim.isReactNative()) {
- context.css['hljs'] = shim.loadCssFromJs(options.codeTheme);
- } else {
- context.cssFiles['hljs'] = `highlight/styles/${options.codeTheme}`;
- }
-
- return `${hlCode}
`;
- } catch (error) {
- return `${markdownIt.utils.escapeHtml(str)}
`;
- }
- },
- });
-
- // To add a plugin, there are three options:
- //
- // 1. If the plugin does not need any application specific data, use the standard way:
- //
- // const someMarkdownPlugin = require('someMarkdownPlugin');
- // markdownIt.use(someMarkdownPlugin);
- //
- // 2. If the plugin does not need any application specific data, and you want the user
- // to be able to toggle the plugin:
- //
- // Add the plugin to the plugins object
- // const plugins = {
- // plugin: require('someMarkdownPlugin'),
- // }
- //
- // And add a corresponding entry into Setting.js
- // 'markdown.plugin.mark': {value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => _('Enable ==mark== syntax')},
- //
- // 3. If the plugin needs application data (in ruleOptions) or needs to pass data (CSS, files to load, etc.) back
- // to the application (using the context object), use the application-specific way:
- //
- // const imagePlugin = require('./MdToHtml/rules/image');
- // markdownIt.use(imagePlugin(context, ruleOptions));
- //
- // Using the `context` object, a plugin can send back either CSS strings (in .css) or CSS files that need
- // to be loaded (in .cssFiles). In general, the desktop app will load the CSS files and the mobile app
- // will load the CSS strings.
-
- markdownIt.use(rules.image(context, ruleOptions));
- markdownIt.use(rules.checkbox(context, ruleOptions));
- markdownIt.use(rules.link_open(context, ruleOptions));
- markdownIt.use(rules.html_image(context, ruleOptions));
- if (Setting.value('markdown.plugin.katex')) markdownIt.use(rules.katex(context, ruleOptions));
- if (Setting.value('markdown.plugin.fountain')) markdownIt.use(rules.fountain(context, ruleOptions));
- markdownIt.use(rules.highlight_keywords(context, ruleOptions));
- markdownIt.use(rules.code_inline(context, ruleOptions));
- markdownIt.use(markdownItAnchor, { slugify: uslugify });
-
- for (let key in plugins) {
- if (Setting.value(`markdown.plugin.${key}`)) markdownIt.use(plugins[key].module, plugins[key].options);
- }
-
- setupLinkify(markdownIt);
-
- const renderedBody = markdownIt.render(body);
-
- const cssStrings = noteStyle(style, options);
-
- for (let k in context.css) {
- if (!context.css.hasOwnProperty(k)) continue;
- cssStrings.push(context.css[k]);
- }
-
- for (let k in context.assetLoaders) {
- if (!context.assetLoaders.hasOwnProperty(k)) continue;
- context.assetLoaders[k]().catch(error => {
- console.warn(`MdToHtml: Error loading assets for ${k}: `, error.message);
- });
- }
-
- if (options.userCss) cssStrings.push(options.userCss);
-
- const styleHtml = ``;
-
- const html = `${styleHtml}${renderedBody}
`;
-
- const output = {
- html: html,
- cssFiles: Object.keys(context.cssFiles).map(k => context.cssFiles[k]),
- };
-
- // Fow now, we keep only the last entry in the cache
- this.cachedOutputs_ = {};
- this.cachedOutputs_[cacheKey] = output;
-
- return output;
- }
-
- injectedJavaScript() {
- return '';
- }
-}
-
-module.exports = MdToHtml;
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/checkbox.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/checkbox.js
deleted file mode 100644
index ed74e6ef2..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/checkbox.js
+++ /dev/null
@@ -1,126 +0,0 @@
-let checkboxIndex_ = -1;
-
-const checkboxStyle = `
- /* Remove the indentation from the checkboxes at the root of the document
- (otherwise they are too far right), but keep it for their children to allow
- nested lists. Make sure this value matches the UL margin. */
-
- #rendered-md > ul > li.md-checkbox {
- margin-left: -1.7em;
- }
-
- li.md-checkbox {
- list-style-type: none;
- }
-
- li.md-checkbox input[type=checkbox] {
- margin-right: 1em;
- }
-`;
-
-function createPrefixTokens(Token, id, checked, label, postMessageSyntax, sourceToken) {
- let token = null;
- const tokens = [];
-
- // A bit hard to handle errors here and it's unlikely that the token won't have a valid
- // map parameter, but if it does set it to a very high value, which will be more easy to notice
- // in calling code.
- const lineIndex = sourceToken.map && sourceToken.map.length ? sourceToken.map[0] : 99999999;
- const checkedString = checked ? 'checked' : 'unchecked';
-
- const labelId = `cb-label-${id}`;
-
- const js = `
- ${postMessageSyntax}('checkboxclick:${checkedString}:${lineIndex}');
- const label = document.getElementById("${labelId}");
- label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
- label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
- return true;
- `;
-
- token = new Token('checkbox_input', 'input', 0);
- token.attrs = [['type', 'checkbox'], ['id', id], ['onclick', js]];
- if (checked) token.attrs.push(['checked', 'true']);
- tokens.push(token);
-
- token = new Token('label_open', 'label', 1);
- token.attrs = [['id', labelId], ['for', id], ['class', `checkbox-label-${checkedString}`]];
- tokens.push(token);
-
- if (label) {
- token = new Token('text', '', 0);
- token.content = label;
- tokens.push(token);
- }
-
- return tokens;
-}
-
-function createSuffixTokens(Token) {
- return [new Token('label_close', 'label', -1)];
-}
-
-function installRule(markdownIt, mdOptions, ruleOptions, context) {
- markdownIt.core.ruler.push('checkbox', state => {
- const tokens = state.tokens;
- const Token = state.Token;
-
- const checkboxPattern = /^\[([x|X| ])\] (.*)$/;
- let currentListItem = null;
- let processedFirstInline = false;
- for (let i = 0; i < tokens.length; i++) {
- const token = tokens[i];
-
- if (token.type === 'list_item_open') {
- currentListItem = token;
- processedFirstInline = false;
- continue;
- }
-
- if (token.type === 'list_item_close') {
- currentListItem = null;
- processedFirstInline = false;
- continue;
- }
-
- // Note that we only support list items that start with "-" (not with "*")
- if (currentListItem && currentListItem.markup === '-' && !processedFirstInline && token.type === 'inline') {
- processedFirstInline = true;
- const firstChild = token.children && token.children.length ? token.children[0] : null;
- if (!firstChild) continue;
-
- const matches = checkboxPattern.exec(firstChild.content);
- if (!matches || matches.length < 2) continue;
-
- checkboxIndex_++;
- const checked = matches[1] !== ' ';
- const id = `md-checkbox-${checkboxIndex_}`;
- const label = matches.length >= 3 ? matches[2] : '';
-
- // Prepend the text content with the checkbox markup and the opening tag
- // then append the tag at the end of the text content.
-
- const prefix = createPrefixTokens(Token, id, checked, label, ruleOptions.postMessageSyntax, token);
- const suffix = createSuffixTokens(Token);
-
- token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, prefix);
- token.children = token.children.concat(suffix);
-
- // Add a class to the container so that it can be targetted with CSS.
-
- let itemClass = currentListItem.attrGet('class');
- if (!itemClass) itemClass = '';
- itemClass += ' md-checkbox';
- currentListItem.attrSet('class', itemClass.trim());
-
- context.css['checkbox'] = checkboxStyle;
- }
- }
- });
-}
-
-module.exports = function(context, ruleOptions) {
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions, context);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/code_inline.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/code_inline.js
deleted file mode 100644
index c0711d2d2..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/code_inline.js
+++ /dev/null
@@ -1,22 +0,0 @@
-function installRule(markdownIt) {
- const defaultRender =
- markdownIt.renderer.rules.code_inline ||
- function(tokens, idx, options, env, self) {
- return self.renderToken(tokens, idx, options);
- };
-
- markdownIt.renderer.rules.code_inline = (tokens, idx, options, env, self) => {
- const token = tokens[idx];
- let tokenClass = token.attrGet('class');
- if (!tokenClass) tokenClass = '';
- tokenClass += ' inline-code';
- token.attrSet('class', tokenClass.trim());
- return defaultRender(tokens, idx, options, env, self);
- };
-}
-
-module.exports = function(context, ruleOptions) {
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/fountain.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/fountain.js
deleted file mode 100644
index fefc8e6b4..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/fountain.js
+++ /dev/null
@@ -1,134 +0,0 @@
-const fountain = require('lib/vendor/fountain.min.js');
-
-const fountainCss = `
-.fountain {
- font-family: monospace;
- line-height: 107%;
- max-width: 1000px;
- margin-left: auto;
- margin-right: auto;
-}
-
-.fountain .title-page,
-.fountain .page {
- box-shadow: 0 0 5px rgba(0,0,0,0.1);
- border: 1px solid #d2d2d2;
- padding: 10%;
- margin-bottom: 2em;
-}
-
-.fountain h1,
-.fountain h2,
-.fountain h3,
-.fountain h4,
-.fountain p {
- font-weight: normal;
- line-height: 107%;
- margin: 1em 0;
- border: none;
- font-size: 1em;
-}
-
-.fountain .bold {
- font-weight: bold;
-}
-
-.fountain .underline {
- text-decoration: underline;
-}
-
-.fountain .centered {
- text-align: center;
-}
-
-.fountain h2 {
- text-align: right;
-}
-
-.fountain .dialogue p.parenthetical {
- margin-left: 11%;
-}
-
-.fountain .title-page .credit,
-.fountain .title-page .authors,
-.fountain .title-page .source {
- text-align: center;
-}
-
-.fountain .title-page h1 {
- margin-bottom: 1.5em;
- text-align: center;
-}
-
-.fountain .title-page .source {
- margin-top: 1.5em;
-}
-
-.fountain .title-page .notes {
- text-align: right;
- margin: 3em 0;
-}
-
-.fountain .title-page h1 {
- margin-bottom: 1.5em;
- text-align: center;
-}
-
-.fountain .dialogue {
- margin-left: 3em;
- margin-right: 3em;
-}
-
-.fountain .dialogue p,
-.fountain .dialogue h1,
-.fountain .dialogue h2,
-.fountain .dialogue h3,
-.fountain .dialogue h4 {
- margin: 0;
-}
-
-.fountain .dialogue h1,
-.fountain .dialogue h2,
-.fountain .dialogue h3,
-.fountain .dialogue h4 {
- text-align: center;
-}
-`;
-
-function renderFountainScript(content) {
- const result = fountain.parse(content);
- return `
-
-
- ${result.html.title_page}
-
-
- ${result.html.script}
-
-
- `;
-}
-
-function addContextAssets(context) {
- if ('fountain' in context.css) return;
- context.css['fountain'] = fountainCss;
-}
-
-function installRule(markdownIt, mdOptions, ruleOptions, context) {
- const defaultRender = markdownIt.renderer.rules.fence || function(tokens, idx, options, env, self) {
- return self.renderToken(tokens, idx, options);
- };
-
- markdownIt.renderer.rules.fence = function(tokens, idx, options, env, self) {
- const token = tokens[idx];
- if (token.info !== 'fountain') return defaultRender(tokens, idx, options, env, self);
- addContextAssets(context);
- return renderFountainScript(token.content);
- };
-}
-
-module.exports = function(context, ruleOptions) {
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions, context);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/highlight_keywords.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/highlight_keywords.js
deleted file mode 100644
index 252cb711c..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/highlight_keywords.js
+++ /dev/null
@@ -1,67 +0,0 @@
-const StringUtils = require('lib/string-utils.js');
-const md5 = require('md5');
-
-function createHighlightedTokens(Token, splitted) {
- let token;
- const output = [];
-
- for (let i = 0; i < splitted.length; i++) {
- const text = splitted[i];
- if (!text) continue;
-
- if (i % 2 === 0) {
- token = new Token('text', '', 0);
- token.content = text;
- output.push(token);
- } else {
- token = new Token('highlighted_keyword_open', 'span', 1);
- token.attrs = [['class', 'highlighted-keyword']];
- output.push(token);
-
- token = new Token('text', '', 0);
- token.content = text;
- output.push(token);
-
- token = new Token('highlighted_keyword_close', 'span', -1);
- output.push(token);
- }
- }
-
- return output;
-}
-
-function installRule(markdownIt, mdOptions, ruleOptions) {
- const divider = md5(Date.now().toString() + Math.random().toString());
-
- markdownIt.core.ruler.push('highlight_keywords', state => {
- const keywords = ruleOptions.highlightedKeywords;
- if (!keywords || !keywords.length) return;
-
- const tokens = state.tokens;
- const Token = state.Token;
-
- for (let i = 0; i < tokens.length; i++) {
- const token = tokens[i];
-
- if (token.type !== 'inline') continue;
-
- for (let j = 0; j < token.children.length; j++) {
- const child = token.children[j];
- if (child.type !== 'text') continue;
-
- const splitted = StringUtils.surroundKeywords(keywords, child.content, divider, divider).split(divider);
- const splittedTokens = createHighlightedTokens(Token, splitted);
- if (splittedTokens.length <= 1) continue;
-
- token.children = markdownIt.utils.arrayReplaceAt(token.children, j, splittedTokens);
- j += splittedTokens.length - 1;
- }
- }
- });
-}
-
-module.exports = function(context, ruleOptions) {
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/html_image.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/html_image.js
deleted file mode 100644
index ea6ddc63b..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/html_image.js
+++ /dev/null
@@ -1,51 +0,0 @@
-const Resource = require('lib/models/Resource.js');
-const htmlUtils = require('lib/htmlUtils.js');
-const utils = require('../../utils');
-
-function renderImageHtml(before, src, after, ruleOptions) {
- const r = utils.imageReplacement(src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
- if (typeof r === 'string') return r;
- if (r) return ` `;
- return `[Image: ${src}]`;
-}
-
-function installRule(markdownIt, mdOptions, ruleOptions) {
- const htmlBlockDefaultRender =
- markdownIt.renderer.rules.html_block ||
- function(tokens, idx, options, env, self) {
- return self.renderToken(tokens, idx, options);
- };
-
- const htmlInlineDefaultRender =
- markdownIt.renderer.rules.html_inline ||
- function(tokens, idx, options, env, self) {
- return self.renderToken(tokens, idx, options);
- };
-
- const imageRegex = / /gi;
-
- const handleImageTags = function(defaultRender) {
- return function(tokens, idx, options, env, self) {
- const token = tokens[idx];
- const content = token.content;
-
- if (!content.match(imageRegex)) return defaultRender(tokens, idx, options, env, self);
-
- return content.replace(imageRegex, (v, before, src, after) => {
- if (!Resource.isResourceUrl(src)) return defaultRender(tokens, idx, options, env, self);
- return renderImageHtml(before, src, after, ruleOptions);
- });
- };
- };
-
- // It seems images sometimes are inline, sometimes a block
- // to make sure they both render correctly.
- markdownIt.renderer.rules.html_block = handleImageTags(htmlBlockDefaultRender);
- markdownIt.renderer.rules.html_inline = handleImageTags(htmlInlineDefaultRender);
-}
-
-module.exports = function(context, ruleOptions) {
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/image.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/image.js
deleted file mode 100644
index 6f9c09b84..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/image.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const Resource = require('lib/models/Resource.js');
-const utils = require('../../utils');
-const htmlUtils = require('lib/htmlUtils.js');
-
-function installRule(markdownIt, mdOptions, ruleOptions) {
- const defaultRender = markdownIt.renderer.rules.image;
-
- markdownIt.renderer.rules.image = (tokens, idx, options, env, self) => {
- const token = tokens[idx];
- const src = utils.getAttr(token.attrs, 'src');
- const title = utils.getAttr(token.attrs, 'title');
-
- if (!Resource.isResourceUrl(src) || ruleOptions.plainResourceRendering) return defaultRender(tokens, idx, options, env, self);
-
- const r = utils.imageReplacement(src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
- if (typeof r === 'string') return r;
- if (r) return ` `;
-
- return defaultRender(tokens, idx, options, env, self);
- };
-}
-
-module.exports = function(context, ruleOptions) {
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/katex.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/katex.js
deleted file mode 100644
index 894ec3d25..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/katex.js
+++ /dev/null
@@ -1,295 +0,0 @@
-// Based on https://github.com/waylonflinn/markdown-it-katex
-
-'use strict';
-
-const { shim } = require('lib/shim');
-const Setting = require('lib/models/Setting');
-var katex = require('katex');
-const katexCss = require('lib/csstojs/katex.css.js');
-const md5 = require('md5');
-const mhchemModule = require('./katex_mhchem.js');
-
-katex = mhchemModule(katex);
-
-// const style = `
-// /*
-// This is to fix https://github.com/laurent22/joplin/issues/764
-// Without this, the tag attached to an equation float at an absolute position of the page,
-// instead of a position relative to the container.
-// 2018-03-13: No longer needed??
-// */
-
-// /*
-// .katex-display>.katex>.katex-html {
-// position: relative;
-// }
-// */
-// `
-
-// Test if potential opening or closing delimieter
-// Assumes that there is a "$" at state.src[pos]
-function isValidDelim(state, pos) {
- var prevChar,
- nextChar,
- max = state.posMax,
- can_open = true,
- can_close = true;
-
- prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
- nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
-
- // Check non-whitespace conditions for opening and closing, and
- // check that closing delimeter isn't followed by a number
- if (prevChar === 0x20 /* " " */ || prevChar === 0x09 /* \t */ || (nextChar >= 0x30 /* "0" */ && nextChar <= 0x39) /* "9" */) {
- can_close = false;
- }
- if (nextChar === 0x20 /* " " */ || nextChar === 0x09 /* \t */) {
- can_open = false;
- }
-
- return {
- can_open: can_open,
- can_close: can_close,
- };
-}
-
-function math_inline(state, silent) {
- var start, match, token, res, pos;
-
- if (state.src[state.pos] !== '$') {
- return false;
- }
-
- res = isValidDelim(state, state.pos);
- if (!res.can_open) {
- if (!silent) {
- state.pending += '$';
- }
- state.pos += 1;
- return true;
- }
-
- // First check for and bypass all properly escaped delimieters
- // This loop will assume that the first leading backtick can not
- // be the first character in state.src, which is known since
- // we have found an opening delimieter already.
- start = state.pos + 1;
- match = start;
- while ((match = state.src.indexOf('$', match)) !== -1) {
- // Found potential $, look for escapes, pos will point to
- // first non escape when complete
- pos = match - 1;
- while (state.src[pos] === '\\') {
- pos -= 1;
- }
-
- // Even number of escapes, potential closing delimiter found
- if ((match - pos) % 2 == 1) {
- break;
- }
- match += 1;
- }
-
- // No closing delimter found. Consume $ and continue.
- if (match === -1) {
- if (!silent) {
- state.pending += '$';
- }
- state.pos = start;
- return true;
- }
-
- // Check if we have empty content, ie: $$. Do not parse.
- if (match - start === 0) {
- if (!silent) {
- state.pending += '$$';
- }
- state.pos = start + 1;
- return true;
- }
-
- // Check for valid closing delimiter
- res = isValidDelim(state, match);
- if (!res.can_close) {
- if (!silent) {
- state.pending += '$';
- }
- state.pos = start;
- return true;
- }
-
- if (!silent) {
- token = state.push('math_inline', 'math', 0);
- token.markup = '$';
- token.content = state.src.slice(start, match);
- }
-
- state.pos = match + 1;
- return true;
-}
-
-function math_block(state, start, end, silent) {
- var firstLine,
- lastLine,
- next,
- lastPos,
- found = false,
- token,
- pos = state.bMarks[start] + state.tShift[start],
- max = state.eMarks[start];
-
- if (pos + 2 > max) {
- return false;
- }
- if (state.src.slice(pos, pos + 2) !== '$$') {
- return false;
- }
-
- pos += 2;
- firstLine = state.src.slice(pos, max);
-
- if (silent) {
- return true;
- }
- if (firstLine.trim().slice(-2) === '$$') {
- // Single line expression
- firstLine = firstLine.trim().slice(0, -2);
- found = true;
- }
-
- for (next = start; !found;) {
- next++;
-
- if (next >= end) {
- break;
- }
-
- pos = state.bMarks[next] + state.tShift[next];
- max = state.eMarks[next];
-
- if (pos < max && state.tShift[next] < state.blkIndent) {
- // non-empty line with negative indent should stop the list:
- break;
- }
-
- if (
- state.src
- .slice(pos, max)
- .trim()
- .slice(-2) === '$$'
- ) {
- lastPos = state.src.slice(0, max).lastIndexOf('$$');
- lastLine = state.src.slice(pos, lastPos);
- found = true;
- }
- }
-
- state.line = next + 1;
-
- token = state.push('math_block', 'math', 0);
- token.block = true;
- token.content = (firstLine && firstLine.trim() ? `${firstLine}\n` : '') + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : '');
- token.map = [start, state.line];
- token.markup = '$$';
- return true;
-}
-
-let assetsLoaded_ = false;
-let cache_ = {};
-
-module.exports = function(context) {
- // Keep macros that persist across Katex blocks to allow defining a macro
- // in one block and re-using it later in other blocks.
- // https://github.com/laurent22/joplin/issues/1105
- context.__katex = { macros: {} };
-
- const addContextAssets = () => {
- context.css['katex'] = katexCss;
- context.assetLoaders['katex'] = async () => {
- if (assetsLoaded_) return;
-
- // In node, the fonts are simply copied using copycss to where Katex expects to find them, which is under app/gui/note-viewer/fonts
-
- // In React Native, it's more complicated and we need to download and copy them to the right directory. Ideally, we should embed
- // them as an asset and copy them from there (or load them from there by modifying Katex CSS), but for now that will do.
-
- if (shim.isReactNative()) {
- // Fonts must go under the resourceDir directory because this is the baseUrl of NoteBodyViewer
- const baseDir = Setting.value('resourceDir');
- await shim.fsDriver().mkdir(`${baseDir}/fonts`);
-
- await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Main-Regular.woff2', { overwrite: false, path: `${baseDir}/fonts/KaTeX_Main-Regular.woff2` });
- await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Math-Italic.woff2', { overwrite: false, path: `${baseDir}/fonts/KaTeX_Math-Italic.woff2` });
- await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Size1-Regular.woff2', { overwrite: false, path: `${baseDir}/fonts/KaTeX_Size1-Regular.woff2` });
- }
-
- // eslint-disable-next-line require-atomic-updates
- assetsLoaded_ = true;
- };
- };
-
- function renderToStringWithCache(latex, options) {
- const cacheKey = md5(escape(latex) + escape(JSON.stringify(options)));
- if (cacheKey in cache_) {
- return cache_[cacheKey];
- } else {
- const beforeMacros = JSON.stringify(options.macros);
- const output = katex.renderToString(latex, options);
- const afterMacros = JSON.stringify(options.macros);
-
- // Don't cache the formulas that add macros, otherwise
- // they won't be added on second run.
- if (beforeMacros === afterMacros) cache_[cacheKey] = output;
- return output;
- }
- }
-
- return function(md, options) {
- // Default options
-
- options = options || {};
- options.macros = context.__katex.macros;
-
- // set KaTeX as the renderer for markdown-it-simplemath
- var katexInline = function(latex) {
- options.displayMode = false;
- try {
- return renderToStringWithCache(latex, options);
- } catch (error) {
- if (options.throwOnError) {
- console.log(error);
- }
- return latex;
- }
- };
-
- var inlineRenderer = function(tokens, idx) {
- addContextAssets();
- return katexInline(tokens[idx].content);
- };
-
- var katexBlock = function(latex) {
- options.displayMode = true;
- try {
- return `${renderToStringWithCache(latex, options)}
`;
- } catch (error) {
- if (options.throwOnError) {
- console.log(error);
- }
- return latex;
- }
- };
-
- var blockRenderer = function(tokens, idx) {
- addContextAssets();
- return `${katexBlock(tokens[idx].content)}\n`;
- };
-
- md.inline.ruler.after('escape', 'math_inline', math_inline);
- md.block.ruler.after('blockquote', 'math_block', math_block, {
- alt: ['paragraph', 'reference', 'blockquote', 'list'],
- });
- md.renderer.rules.math_inline = inlineRenderer;
- md.renderer.rules.math_block = blockRenderer;
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/katex_mhchem.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/katex_mhchem.js
deleted file mode 100644
index 97887e4f8..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/katex_mhchem.js
+++ /dev/null
@@ -1,1732 +0,0 @@
-/* eslint-disable */
-
-
-
-
-
-// 2018-10-13:
-// This file is generated from mhchem.js by wrapping it in a function
-// so that it can be imported in the various Joplin apps.
-// To update it, paste the content of mhchem.js file inside the markers below.
-// The file is at https://github.com/KaTeX/KaTeX/blob/master/contrib/mhchem/mhchem.js
-
-
-
-
-
-
-
-/*************************************************************
- *
- * KaTeX mhchem.js
- *
- * This file implements a KaTeX version of mhchem version 3.3.0.
- * It is adapted from MathJax/extensions/TeX/mhchem.js
- * It differs from the MathJax version as follows:
- * 1. The interface is changed so that it can be called from KaTeX, not MathJax.
- * 2. \rlap and \llap are replaced with \mathrlap and \mathllap.
- * 3. Four lines of code are edited in order to use \raisebox instead of \raise.
- * 4. The reaction arrow code is simplified. All reaction arrows are rendered
- * using KaTeX extensible arrows instead of building non-extensible arrows.
- * 5. \tripledash vertical alignment is slightly adjusted.
- *
- * This code, as other KaTeX code, is released under the MIT license.
- *
- * /*************************************************************
- *
- * MathJax/extensions/TeX/mhchem.js
- *
- * Implements the \ce command for handling chemical formulas
- * from the mhchem LaTeX package.
- *
- * ---------------------------------------------------------------------
- *
- * Copyright (c) 2011-2015 The MathJax Consortium
- * Copyright (c) 2015-2018 Martin Hensel
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Coding Style
-// - use '' for identifiers that can by minified/uglified
-// - use "" for strings that need to stay untouched
-
-// version: "3.3.0" for MathJax and KaTeX
-
-
-// Add \ce, \pu, and \tripledash to the KaTeX macros.
-
-var mhchemModule = function(katex) {
-
- // *************************************************************************
- // *************************************************************************
- // Paste https://github.com/KaTeX/KaTeX/blob/master/contrib/mhchem/mhchem.js
- // *************************************************************************
- // *************************************************************************
-
- katex.__defineMacro("\\ce", function(context) {
- return chemParse(context.consumeArgs(1)[0], "ce")
- });
-
- katex.__defineMacro("\\pu", function(context) {
- return chemParse(context.consumeArgs(1)[0], "pu");
- });
-
- // Needed for \bond for the ~ forms
- // Raise by 2.56mu, not 2mu. We're raising a hyphen-minus, U+002D, not
- // a mathematical minus, U+2212. So we need that extra 0.56.
- katex.__defineMacro("\\tripledash", "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu"
- + "\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}");
-
- //
- // This is the main function for handing the \ce and \pu commands.
- // It takes the argument to \ce or \pu and returns the corresponding TeX string.
- //
-
- var chemParse = function (tokens, stateMachine) {
- // Recreate the argument string from KaTeX's array of tokens.
- var str = "";
- var expectedLoc = tokens[tokens.length - 1].loc.start
- for (var i = tokens.length - 1; i >= 0; i--) {
- if(tokens[i].loc.start > expectedLoc) {
- // context.consumeArgs has eaten a space.
- str += " ";
- expectedLoc = tokens[i].loc.start;
- }
- str += tokens[i].text;
- expectedLoc += tokens[i].text.length;
- }
- var tex = texify.go(mhchemParser.go(str, stateMachine));
- return tex;
- };
-
- //
- // Core parser for mhchem syntax (recursive)
- //
- /** @type {MhchemParser} */
- var mhchemParser = {
- //
- // Parses mchem \ce syntax
- //
- // Call like
- // go("H2O");
- //
- go: function (input, stateMachine) {
- if (!input) { return []; }
- if (stateMachine === undefined) { stateMachine = 'ce'; }
- var state = '0';
-
- //
- // String buffers for parsing:
- //
- // buffer.a == amount
- // buffer.o == element
- // buffer.b == left-side superscript
- // buffer.p == left-side subscript
- // buffer.q == right-side subscript
- // buffer.d == right-side superscript
- //
- // buffer.r == arrow
- // buffer.rdt == arrow, script above, type
- // buffer.rd == arrow, script above, content
- // buffer.rqt == arrow, script below, type
- // buffer.rq == arrow, script below, content
- //
- // buffer.text_
- // buffer.rm
- // etc.
- //
- // buffer.parenthesisLevel == int, starting at 0
- // buffer.sb == bool, space before
- // buffer.beginsWithBond == bool
- //
- // These letters are also used as state names.
- //
- // Other states:
- // 0 == begin of main part (arrow/operator unlikely)
- // 1 == next entity
- // 2 == next entity (arrow/operator unlikely)
- // 3 == next atom
- // c == macro
- //
- /** @type {Buffer} */
- var buffer = {};
- buffer['parenthesisLevel'] = 0;
-
- input = input.replace(/\n/g, " ");
- input = input.replace(/[\u2212\u2013\u2014\u2010]/g, "-");
- input = input.replace(/[\u2026]/g, "...");
-
- //
- // Looks through mhchemParser.transitions, to execute a matching action
- // (recursive)
- //
- var lastInput;
- var watchdog = 10;
- /** @type {ParserOutput[]} */
- var output = [];
- while (true) {
- if (lastInput !== input) {
- watchdog = 10;
- lastInput = input;
- } else {
- watchdog--;
- }
- //
- // Find actions in transition table
- //
- var machine = mhchemParser.stateMachines[stateMachine];
- var t = machine.transitions[state] || machine.transitions['*'];
- iterateTransitions:
- for (var i=0; i 0) {
- if (!task.revisit) {
- input = matches.remainder;
- }
- if (!task.toContinue) {
- break iterateTransitions;
- }
- } else {
- return output;
- }
- }
- }
- //
- // Prevent infinite loop
- //
- if (watchdog <= 0) {
- throw ["MhchemBugU", "mhchem bug U. Please report."]; // Unexpected character
- }
- }
- },
- concatArray: function (a, b) {
- if (b) {
- if (Array.isArray(b)) {
- for (var iB=0; iB': /^[=<>]/,
- '#': /^[#\u2261]/,
- '+': /^\+/,
- '-$': /^-(?=[\s_},;\]/]|$|\([a-z]+\))/, // -space -, -; -] -/ -$ -state-of-aggregation
- '-9': /^-(?=[0-9])/,
- '- orbital overlap': /^-(?=(?:[spd]|sp)(?:$|[\s,;\)\]\}]))/,
- '-': /^-/,
- 'pm-operator': /^(?:\\pm|\$\\pm\$|\+-|\+\/-)/,
- 'operator': /^(?:\+|(?:[\-=<>]|<<|>>|\\approx|\$\\approx\$)(?=\s|$|-?[0-9]))/,
- 'arrowUpDown': /^(?:v|\(v\)|\^|\(\^\))(?=$|[\s,;\)\]\}])/,
- '\\bond{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\bond{", "", "", "}"); },
- '->': /^(?:<->|<-->|->|<-|<=>>|<<=>|<=>|[\u2192\u27F6\u21CC])/,
- 'CMT': /^[CMT](?=\[)/,
- '[(...)]': function (input) { return mhchemParser.patterns.findObserveGroups(input, "[", "", "", "]"); },
- '1st-level escape': /^(&|\\\\|\\hline)\s*/,
- '\\,': /^(?:\\[,\ ;:])/, // \\x - but output no space before
- '\\x{}{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", "", "", "{", "}", "", true); },
- '\\x{}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "", /^\\[a-zA-Z]+\{/, "}", ""); },
- '\\ca': /^\\ca(?:\s+|(?![a-zA-Z]))/,
- '\\x': /^(?:\\[a-zA-Z]+\s*|\\[_&{}%])/,
- 'orbital': /^(?:[0-9]{1,2}[spdfgh]|[0-9]{0,2}sp)(?=$|[^a-zA-Z])/, // only those with numbers in front, because the others will be formatted correctly anyway
- 'others': /^[\/~|]/,
- '\\frac{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\frac{", "", "", "}", "{", "", "", "}"); },
- '\\overset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\overset{", "", "", "}", "{", "", "", "}"); },
- '\\underset{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underset{", "", "", "}", "{", "", "", "}"); },
- '\\underbrace{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\underbrace{", "", "", "}_", "{", "", "", "}"); },
- '\\color{(...)}0': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}"); },
- '\\color{(...)}{(...)}1': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color{", "", "", "}", "{", "", "", "}"); },
- '\\color(...){(...)}2': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\color", "\\", "", /^(?=\{)/, "{", "", "", "}"); },
- '\\ce{(...)}': function (input) { return mhchemParser.patterns.findObserveGroups(input, "\\ce{", "", "", "}"); },
- 'oxidation$': /^(?:[+-][IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/,
- 'd-oxidation$': /^(?:[+-]?\s?[IVX]+|\\pm\s*0|\$\\pm\$\s*0)$/, // 0 could be oxidation or charge
- 'roman numeral': /^[IVX]+/,
- '1/2$': /^[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+(?:\$[a-z]\$|[a-z])?$/,
- 'amount': function (input) {
- var match;
- // e.g. 2, 0.5, 1/2, -2, n/2, +; $a$ could be added later in parsing
- match = input.match(/^(?:(?:(?:\([+\-]?[0-9]+\/[0-9]+\)|[+\-]?(?:[0-9]+|\$[a-z]\$|[a-z])\/[0-9]+|[+\-]?[0-9]+[.,][0-9]+|[+\-]?\.[0-9]+|[+\-]?[0-9]+)(?:[a-z](?=\s*[A-Z]))?)|[+\-]?[a-z](?=\s*[A-Z])|\+(?!\s))/);
- if (match) {
- return { match_: match[0], remainder: input.substr(match[0].length) };
- }
- var a = mhchemParser.patterns.findObserveGroups(input, "", "$", "$", "");
- if (a) { // e.g. $2n-1$, $-$
- match = a.match_.match(/^\$(?:\(?[+\-]?(?:[0-9]*[a-z]?[+\-])?[0-9]*[a-z](?:[+\-][0-9]*[a-z]?)?\)?|\+|-)\$$/);
- if (match) {
- return { match_: match[0], remainder: input.substr(match[0].length) };
- }
- }
- return null;
- },
- 'amount2': function (input) { return this['amount'](input); },
- '(KV letters),': /^(?:[A-Z][a-z]{0,2}|i)(?=,)/,
- 'formula$': function (input) {
- if (input.match(/^\([a-z]+\)$/)) { return null; } // state of aggregation = no formula
- var match = input.match(/^(?:[a-z]|(?:[0-9\ \+\-\,\.\(\)]+[a-z])+[0-9\ \+\-\,\.\(\)]*|(?:[a-z][0-9\ \+\-\,\.\(\)]+)+[a-z]?)$/);
- if (match) {
- return { match_: match[0], remainder: input.substr(match[0].length) };
- }
- return null;
- },
- 'uprightEntities': /^(?:pH|pOH|pC|pK|iPr|iBu)(?=$|[^a-zA-Z])/,
- '/': /^\s*(\/)\s*/,
- '//': /^\s*(\/\/)\s*/,
- '*': /^\s*[*.]\s*/
- },
- findObserveGroups: function (input, begExcl, begIncl, endIncl, endExcl, beg2Excl, beg2Incl, end2Incl, end2Excl, combine) {
- /** @type {{(input: string, pattern: string | RegExp): string | string[] | null;}} */
- var _match = function (input, pattern) {
- if (typeof pattern === "string") {
- if (input.indexOf(pattern) !== 0) { return null; }
- return pattern;
- } else {
- var match = input.match(pattern);
- if (!match) { return null; }
- return match[0];
- }
- };
- /** @type {{(input: string, i: number, endChars: string | RegExp): {endMatchBegin: number, endMatchEnd: number} | null;}} */
- var _findObserveGroups = function (input, i, endChars) {
- var braces = 0;
- while (i < input.length) {
- var a = input.charAt(i);
- var match = _match(input.substr(i), endChars);
- if (match !== null && braces === 0) {
- return { endMatchBegin: i, endMatchEnd: i + match.length };
- } else if (a === "{") {
- braces++;
- } else if (a === "}") {
- if (braces === 0) {
- throw ["ExtraCloseMissingOpen", "Extra close brace or missing open brace"];
- } else {
- braces--;
- }
- }
- i++;
- }
- if (braces > 0) {
- return null;
- }
- return null;
- };
- var match = _match(input, begExcl);
- if (match === null) { return null; }
- input = input.substr(match.length);
- match = _match(input, begIncl);
- if (match === null) { return null; }
- var e = _findObserveGroups(input, match.length, endIncl || endExcl);
- if (e === null) { return null; }
- var match1 = input.substring(0, (endIncl ? e.endMatchEnd : e.endMatchBegin));
- if (!(beg2Excl || beg2Incl)) {
- return {
- match_: match1,
- remainder: input.substr(e.endMatchEnd)
- };
- } else {
- var group2 = this.findObserveGroups(input.substr(e.endMatchEnd), beg2Excl, beg2Incl, end2Incl, end2Excl);
- if (group2 === null) { return null; }
- /** @type {string[]} */
- var matchRet = [match1, group2.match_];
- return {
- match_: (combine ? matchRet.join("") : matchRet),
- remainder: group2.remainder
- };
- }
- },
-
- //
- // Matching function
- // e.g. match("a", input) will look for the regexp called "a" and see if it matches
- // returns null or {match_:"a", remainder:"bc"}
- //
- match_: function (m, input) {
- var pattern = mhchemParser.patterns.patterns[m];
- if (pattern === undefined) {
- throw ["MhchemBugP", "mhchem bug P. Please report. (" + m + ")"]; // Trying to use non-existing pattern
- } else if (typeof pattern === "function") {
- return mhchemParser.patterns.patterns[m](input); // cannot use cached var pattern here, because some pattern functions need this===mhchemParser
- } else { // RegExp
- var match = input.match(pattern);
- if (match) {
- var mm;
- if (match[2]) {
- mm = [ match[1], match[2] ];
- } else if (match[1]) {
- mm = match[1];
- } else {
- mm = match[0];
- }
- return { match_: mm, remainder: input.substr(match[0].length) };
- }
- return null;
- }
- }
- },
-
- //
- // Generic state machine actions
- //
- actions: {
- 'a=': function (buffer, m) { buffer.a = (buffer.a || "") + m; },
- 'b=': function (buffer, m) { buffer.b = (buffer.b || "") + m; },
- 'p=': function (buffer, m) { buffer.p = (buffer.p || "") + m; },
- 'o=': function (buffer, m) { buffer.o = (buffer.o || "") + m; },
- 'q=': function (buffer, m) { buffer.q = (buffer.q || "") + m; },
- 'd=': function (buffer, m) { buffer.d = (buffer.d || "") + m; },
- 'rm=': function (buffer, m) { buffer.rm = (buffer.rm || "") + m; },
- 'text=': function (buffer, m) { buffer.text_ = (buffer.text_ || "") + m; },
- 'insert': function (buffer, m, a) { return { type_: a }; },
- 'insert+p1': function (buffer, m, a) { return { type_: a, p1: m }; },
- 'insert+p1+p2': function (buffer, m, a) { return { type_: a, p1: m[0], p2: m[1] }; },
- 'copy': function (buffer, m) { return m; },
- 'rm': function (buffer, m) { return { type_: 'rm', p1: m || ""}; },
- 'text': function (buffer, m) { return mhchemParser.go(m, 'text'); },
- '{text}': function (buffer, m) {
- var ret = [ "{" ];
- mhchemParser.concatArray(ret, mhchemParser.go(m, 'text'));
- ret.push("}");
- return ret;
- },
- 'tex-math': function (buffer, m) { return mhchemParser.go(m, 'tex-math'); },
- 'tex-math tight': function (buffer, m) { return mhchemParser.go(m, 'tex-math tight'); },
- 'bond': function (buffer, m, k) { return { type_: 'bond', kind_: k || m }; },
- 'color0-output': function (buffer, m) { return { type_: 'color0', color: m[0] }; },
- 'ce': function (buffer, m) { return mhchemParser.go(m); },
- '1/2': function (buffer, m) {
- /** @type {ParserOutput[]} */
- var ret = [];
- if (m.match(/^[+\-]/)) {
- ret.push(m.substr(0, 1));
- m = m.substr(1);
- }
- var n = m.match(/^([0-9]+|\$[a-z]\$|[a-z])\/([0-9]+)(\$[a-z]\$|[a-z])?$/);
- n[1] = n[1].replace(/\$/g, "");
- ret.push({ type_: 'frac', p1: n[1], p2: n[2] });
- if (n[3]) {
- n[3] = n[3].replace(/\$/g, "");
- ret.push({ type_: 'tex-math', p1: n[3] });
- }
- return ret;
- },
- '9,9': function (buffer, m) { return mhchemParser.go(m, '9,9'); }
- },
- //
- // createTransitions
- // convert { 'letter': { 'state': { action_: 'output' } } } to { 'state' => [ { pattern: 'letter', task: { action_: [{type_: 'output'}] } } ] }
- // with expansion of 'a|b' to 'a' and 'b' (at 2 places)
- //
- createTransitions: function (o) {
- var pattern, state;
- /** @type {string[]} */
- var stateArray;
- var i;
- //
- // 1. Collect all states
- //
- /** @type {Transitions} */
- var transitions = {};
- for (pattern in o) {
- for (state in o[pattern]) {
- stateArray = state.split("|");
- o[pattern][state].stateArray = stateArray;
- for (i=0; i [{type_:'text='}]
- // (Note to myself: Resolving the function here would be problematic. It would need .bind (for *this*) and currying (for *option*).)
- //
- /** @type {any} */
- var p = o[pattern][state];
- if (p.action_) {
- p.action_ = [].concat(p.action_);
- for (var k=0; k': {
- '0|1|2|3': { action_: 'r=', nextState: 'r' },
- 'a|as': { action_: [ 'output', 'r=' ], nextState: 'r' },
- '*': { action_: [ 'output', 'r=' ], nextState: 'r' } },
- '+': {
- 'o': { action_: 'd= kv', nextState: 'd' },
- 'd|D': { action_: 'd=', nextState: 'd' },
- 'q': { action_: 'd=', nextState: 'qd' },
- 'qd|qD': { action_: 'd=', nextState: 'qd' },
- 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' },
- '3': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } },
- 'amount': {
- '0|2': { action_: 'a=', nextState: 'a' } },
- 'pm-operator': {
- '0|1|2|a|as': { action_: [ 'sb=false', 'output', { type_: 'operator', option: '\\pm' } ], nextState: '0' } },
- 'operator': {
- '0|1|2|a|as': { action_: [ 'sb=false', 'output', 'operator' ], nextState: '0' } },
- '-$': {
- 'o|q': { action_: [ 'charge or bond', 'output' ], nextState: 'qd' },
- 'd': { action_: 'd=', nextState: 'd' },
- 'D': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' },
- 'q': { action_: 'd=', nextState: 'qd' },
- 'qd': { action_: 'd=', nextState: 'qd' },
- 'qD|dq': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } },
- '-9': {
- '3|o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '3' } },
- '- orbital overlap': {
- 'o': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' },
- 'd': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' } },
- '-': {
- '0|1|2': { action_: [ { type_: 'output', option: 1 }, 'beginsWithBond=true', { type_: 'bond', option: "-" } ], nextState: '3' },
- '3': { action_: { type_: 'bond', option: "-" } },
- 'a': { action_: [ 'output', { type_: 'insert', option: 'hyphen' } ], nextState: '2' },
- 'as': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "-" } ], nextState: '3' },
- 'b': { action_: 'b=' },
- 'o': { action_: { type_: '- after o/d', option: false }, nextState: '2' },
- 'q': { action_: { type_: '- after o/d', option: false }, nextState: '2' },
- 'd|qd|dq': { action_: { type_: '- after o/d', option: true }, nextState: '2' },
- 'D|qD|p': { action_: [ 'output', { type_: 'bond', option: "-" } ], nextState: '3' } },
- 'amount2': {
- '1|3': { action_: 'a=', nextState: 'a' } },
- 'letters': {
- '0|1|2|3|a|as|b|p|bp|o': { action_: 'o=', nextState: 'o' },
- 'q|dq': { action_: ['output', 'o='], nextState: 'o' },
- 'd|D|qd|qD': { action_: 'o after d', nextState: 'o' } },
- 'digits': {
- 'o': { action_: 'q=', nextState: 'q' },
- 'd|D': { action_: 'q=', nextState: 'dq' },
- 'q': { action_: [ 'output', 'o=' ], nextState: 'o' },
- 'a': { action_: 'o=', nextState: 'o' } },
- 'space A': {
- 'b|p|bp': {} },
- 'space': {
- 'a': { nextState: 'as' },
- '0': { action_: 'sb=false' },
- '1|2': { action_: 'sb=true' },
- 'r|rt|rd|rdt|rdq': { action_: 'output', nextState: '0' },
- '*': { action_: [ 'output', 'sb=true' ], nextState: '1'} },
- '1st-level escape': {
- '1|2': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ] },
- '*': { action_: [ 'output', { type_: 'insert+p1', option: '1st-level escape' } ], nextState: '0' } },
- '[(...)]': {
- 'r|rt': { action_: 'rd=', nextState: 'rd' },
- 'rd|rdt': { action_: 'rq=', nextState: 'rdq' } },
- '...': {
- 'o|d|D|dq|qd|qD': { action_: [ 'output', { type_: 'bond', option: "..." } ], nextState: '3' },
- '*': { action_: [ { type_: 'output', option: 1 }, { type_: 'insert', option: 'ellipsis' } ], nextState: '1' } },
- '. |* ': {
- '*': { action_: [ 'output', { type_: 'insert', option: 'addition compound' } ], nextState: '1' } },
- 'state of aggregation $': {
- '*': { action_: [ 'output', 'state of aggregation' ], nextState: '1' } },
- '{[(': {
- 'a|as|o': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' },
- '0|1|2|3': { action_: [ 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' },
- '*': { action_: [ 'output', 'o=', 'output', 'parenthesisLevel++' ], nextState: '2' } },
- ')]}': {
- '0|1|2|3|b|p|bp|o': { action_: [ 'o=', 'parenthesisLevel--' ], nextState: 'o' },
- 'a|as|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=', 'parenthesisLevel--' ], nextState: 'o' } },
- ', ': {
- '*': { action_: [ 'output', 'comma' ], nextState: '0' } },
- '^_': { // ^ and _ without a sensible argument
- '*': { } },
- '^{(...)}|^($...$)': {
- '0|1|2|as': { action_: 'b=', nextState: 'b' },
- 'p': { action_: 'b=', nextState: 'bp' },
- '3|o': { action_: 'd= kv', nextState: 'D' },
- 'q': { action_: 'd=', nextState: 'qD' },
- 'd|D|qd|qD|dq': { action_: [ 'output', 'd=' ], nextState: 'D' } },
- '^a|^\\x{}{}|^\\x{}|^\\x|\'': {
- '0|1|2|as': { action_: 'b=', nextState: 'b' },
- 'p': { action_: 'b=', nextState: 'bp' },
- '3|o': { action_: 'd= kv', nextState: 'd' },
- 'q': { action_: 'd=', nextState: 'qd' },
- 'd|qd|D|qD': { action_: 'd=' },
- 'dq': { action_: [ 'output', 'd=' ], nextState: 'd' } },
- '_{(state of aggregation)}$': {
- 'd|D|q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } },
- '_{(...)}|_($...$)|_9|_\\x{}{}|_\\x{}|_\\x': {
- '0|1|2|as': { action_: 'p=', nextState: 'p' },
- 'b': { action_: 'p=', nextState: 'bp' },
- '3|o': { action_: 'q=', nextState: 'q' },
- 'd|D': { action_: 'q=', nextState: 'dq' },
- 'q|qd|qD|dq': { action_: [ 'output', 'q=' ], nextState: 'q' } },
- '=<>': {
- '0|1|2|3|a|as|o|q|d|D|qd|qD|dq': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: '3' } },
- '#': {
- '0|1|2|3|a|as|o': { action_: [ { type_: 'output', option: 2 }, { type_: 'bond', option: "#" } ], nextState: '3' } },
- '{}': {
- '*': { action_: { type_: 'output', option: 1 }, nextState: '1' } },
- '{...}': {
- '0|1|2|3|a|as|b|p|bp': { action_: 'o=', nextState: 'o' },
- 'o|d|D|q|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } },
- '$...$': {
- 'a': { action_: 'a=' }, // 2$n$
- '0|1|2|3|as|b|p|bp|o': { action_: 'o=', nextState: 'o' }, // not 'amount'
- 'as|o': { action_: 'o=' },
- 'q|d|D|qd|qD|dq': { action_: [ 'output', 'o=' ], nextState: 'o' } },
- '\\bond{(...)}': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'bond' ], nextState: "3" } },
- '\\frac{(...)}': {
- '*': { action_: [ { type_: 'output', option: 1 }, 'frac-output' ], nextState: '3' } },
- '\\overset{(...)}': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'overset-output' ], nextState: '3' } },
- '\\underset{(...)}': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'underset-output' ], nextState: '3' } },
- '\\underbrace{(...)}': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'underbrace-output' ], nextState: '3' } },
- '\\color{(...)}{(...)}1|\\color(...){(...)}2': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'color-output' ], nextState: '3' } },
- '\\color{(...)}0': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'color0-output' ] } },
- '\\ce{(...)}': {
- '*': { action_: [ { type_: 'output', option: 2 }, 'ce' ], nextState: '3' } },
- '\\,': {
- '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '1' } },
- '\\x{}{}|\\x{}|\\x': {
- '0|1|2|3|a|as|b|p|bp|o|c0': { action_: [ 'o=', 'output' ], nextState: '3' },
- '*': { action_: ['output', 'o=', 'output' ], nextState: '3' } },
- 'others': {
- '*': { action_: [ { type_: 'output', option: 1 }, 'copy' ], nextState: '3' } },
- 'else2': {
- 'a': { action_: 'a to o', nextState: 'o', revisit: true },
- 'as': { action_: [ 'output', 'sb=true' ], nextState: '1', revisit: true },
- 'r|rt|rd|rdt|rdq': { action_: [ 'output' ], nextState: '0', revisit: true },
- '*': { action_: [ 'output', 'copy' ], nextState: '3' } }
- }),
- actions: {
- 'o after d': function (buffer, m) {
- var ret;
- if ((buffer.d || "").match(/^[0-9]+$/)) {
- var tmp = buffer.d;
- buffer.d = undefined;
- ret = this['output'](buffer);
- buffer.b = tmp;
- } else {
- ret = this['output'](buffer);
- }
- mhchemParser.actions['o='](buffer, m);
- return ret;
- },
- 'd= kv': function (buffer, m) {
- buffer.d = m;
- buffer.dType = 'kv';
- },
- 'charge or bond': function (buffer, m) {
- if (buffer['beginsWithBond']) {
- /** @type {ParserOutput[]} */
- var ret = [];
- mhchemParser.concatArray(ret, this['output'](buffer));
- mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-"));
- return ret;
- } else {
- buffer.d = m;
- }
- },
- '- after o/d': function (buffer, m, isAfterD) {
- var c1 = mhchemParser.patterns.match_('orbital', buffer.o || "");
- var c2 = mhchemParser.patterns.match_('one lowercase greek letter $', buffer.o || "");
- var c3 = mhchemParser.patterns.match_('one lowercase latin letter $', buffer.o || "");
- var c4 = mhchemParser.patterns.match_('$one lowercase latin letter$ $', buffer.o || "");
- var hyphenFollows = m==="-" && ( c1 && c1.remainder==="" || c2 || c3 || c4 );
- if (hyphenFollows && !buffer.a && !buffer.b && !buffer.p && !buffer.d && !buffer.q && !c1 && c3) {
- buffer.o = '$' + buffer.o + '$';
- }
- /** @type {ParserOutput[]} */
- var ret = [];
- if (hyphenFollows) {
- mhchemParser.concatArray(ret, this['output'](buffer));
- ret.push({ type_: 'hyphen' });
- } else {
- c1 = mhchemParser.patterns.match_('digits', buffer.d || "");
- if (isAfterD && c1 && c1.remainder==='') {
- mhchemParser.concatArray(ret, mhchemParser.actions['d='](buffer, m));
- mhchemParser.concatArray(ret, this['output'](buffer));
- } else {
- mhchemParser.concatArray(ret, this['output'](buffer));
- mhchemParser.concatArray(ret, mhchemParser.actions['bond'](buffer, m, "-"));
- }
- }
- return ret;
- },
- 'a to o': function (buffer) {
- buffer.o = buffer.a;
- buffer.a = undefined;
- },
- 'sb=true': function (buffer) { buffer.sb = true; },
- 'sb=false': function (buffer) { buffer.sb = false; },
- 'beginsWithBond=true': function (buffer) { buffer['beginsWithBond'] = true; },
- 'beginsWithBond=false': function (buffer) { buffer['beginsWithBond'] = false; },
- 'parenthesisLevel++': function (buffer) { buffer['parenthesisLevel']++; },
- 'parenthesisLevel--': function (buffer) { buffer['parenthesisLevel']--; },
- 'state of aggregation': function (buffer, m) {
- return { type_: 'state of aggregation', p1: mhchemParser.go(m, 'o') };
- },
- 'comma': function (buffer, m) {
- var a = m.replace(/\s*$/, '');
- var withSpace = (a !== m);
- if (withSpace && buffer['parenthesisLevel'] === 0) {
- return { type_: 'comma enumeration L', p1: a };
- } else {
- return { type_: 'comma enumeration M', p1: a };
- }
- },
- 'output': function (buffer, m, entityFollows) {
- // entityFollows:
- // undefined = if we have nothing else to output, also ignore the just read space (buffer.sb)
- // 1 = an entity follows, never omit the space if there was one just read before (can only apply to state 1)
- // 2 = 1 + the entity can have an amount, so output a\, instead of converting it to o (can only apply to states a|as)
- /** @type {ParserOutput | ParserOutput[]} */
- var ret;
- if (!buffer.r) {
- ret = [];
- if (!buffer.a && !buffer.b && !buffer.p && !buffer.o && !buffer.q && !buffer.d && !entityFollows) {
- //ret = [];
- } else {
- if (buffer.sb) {
- ret.push({ type_: 'entitySkip' });
- }
- if (!buffer.o && !buffer.q && !buffer.d && !buffer.b && !buffer.p && entityFollows!==2) {
- buffer.o = buffer.a;
- buffer.a = undefined;
- } else if (!buffer.o && !buffer.q && !buffer.d && (buffer.b || buffer.p)) {
- buffer.o = buffer.a;
- buffer.d = buffer.b;
- buffer.q = buffer.p;
- buffer.a = buffer.b = buffer.p = undefined;
- } else {
- if (buffer.o && buffer.dType==='kv' && mhchemParser.patterns.match_('d-oxidation$', buffer.d || "")) {
- buffer.dType = 'oxidation';
- } else if (buffer.o && buffer.dType==='kv' && !buffer.q) {
- buffer.dType = undefined;
- }
- }
- ret.push({
- type_: 'chemfive',
- a: mhchemParser.go(buffer.a, 'a'),
- b: mhchemParser.go(buffer.b, 'bd'),
- p: mhchemParser.go(buffer.p, 'pq'),
- o: mhchemParser.go(buffer.o, 'o'),
- q: mhchemParser.go(buffer.q, 'pq'),
- d: mhchemParser.go(buffer.d, (buffer.dType === 'oxidation' ? 'oxidation' : 'bd')),
- dType: buffer.dType
- });
- }
- } else { // r
- /** @type {ParserOutput[]} */
- var rd;
- if (buffer.rdt === 'M') {
- rd = mhchemParser.go(buffer.rd, 'tex-math');
- } else if (buffer.rdt === 'T') {
- rd = [ { type_: 'text', p1: buffer.rd || "" } ];
- } else {
- rd = mhchemParser.go(buffer.rd);
- }
- /** @type {ParserOutput[]} */
- var rq;
- if (buffer.rqt === 'M') {
- rq = mhchemParser.go(buffer.rq, 'tex-math');
- } else if (buffer.rqt === 'T') {
- rq = [ { type_: 'text', p1: buffer.rq || ""} ];
- } else {
- rq = mhchemParser.go(buffer.rq);
- }
- ret = {
- type_: 'arrow',
- r: buffer.r,
- rd: rd,
- rq: rq
- };
- }
- for (var p in buffer) {
- if (p !== 'parenthesisLevel' && p !== 'beginsWithBond') {
- delete buffer[p];
- }
- }
- return ret;
- },
- 'oxidation-output': function (buffer, m) {
- var ret = [ "{" ];
- mhchemParser.concatArray(ret, mhchemParser.go(m, 'oxidation'));
- ret.push("}");
- return ret;
- },
- 'frac-output': function (buffer, m) {
- return { type_: 'frac-ce', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
- },
- 'overset-output': function (buffer, m) {
- return { type_: 'overset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
- },
- 'underset-output': function (buffer, m) {
- return { type_: 'underset', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
- },
- 'underbrace-output': function (buffer, m) {
- return { type_: 'underbrace', p1: mhchemParser.go(m[0]), p2: mhchemParser.go(m[1]) };
- },
- 'color-output': function (buffer, m) {
- return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1]) };
- },
- 'r=': function (buffer, m) { buffer.r = m; },
- 'rdt=': function (buffer, m) { buffer.rdt = m; },
- 'rd=': function (buffer, m) { buffer.rd = m; },
- 'rqt=': function (buffer, m) { buffer.rqt = m; },
- 'rq=': function (buffer, m) { buffer.rq = m; },
- 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; }
- }
- },
- 'a': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': {} },
- '1/2$': {
- '0': { action_: '1/2' } },
- 'else': {
- '0': { nextState: '1', revisit: true } },
- '$(...)$': {
- '*': { action_: 'tex-math tight', nextState: '1' } },
- ',': {
- '*': { action_: { type_: 'insert', option: 'commaDecimal' } } },
- 'else2': {
- '*': { action_: 'copy' } }
- }),
- actions: {}
- },
- 'o': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': {} },
- '1/2$': {
- '0': { action_: '1/2' } },
- 'else': {
- '0': { nextState: '1', revisit: true } },
- 'letters': {
- '*': { action_: 'rm' } },
- '\\ca': {
- '*': { action_: { type_: 'insert', option: 'circa' } } },
- '\\x{}{}|\\x{}|\\x': {
- '*': { action_: 'copy' } },
- '${(...)}$|$(...)$': {
- '*': { action_: 'tex-math' } },
- '{(...)}': {
- '*': { action_: '{text}' } },
- 'else2': {
- '*': { action_: 'copy' } }
- }),
- actions: {}
- },
- 'text': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': { action_: 'output' } },
- '{...}': {
- '*': { action_: 'text=' } },
- '${(...)}$|$(...)$': {
- '*': { action_: 'tex-math' } },
- '\\greek': {
- '*': { action_: [ 'output', 'rm' ] } },
- '\\,|\\x{}{}|\\x{}|\\x': {
- '*': { action_: [ 'output', 'copy' ] } },
- 'else': {
- '*': { action_: 'text=' } }
- }),
- actions: {
- 'output': function (buffer) {
- if (buffer.text_) {
- /** @type {ParserOutput} */
- var ret = { type_: 'text', p1: buffer.text_ };
- for (var p in buffer) { delete buffer[p]; }
- return ret;
- }
- }
- }
- },
- 'pq': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': {} },
- 'state of aggregation $': {
- '*': { action_: 'state of aggregation' } },
- 'i$': {
- '0': { nextState: '!f', revisit: true } },
- '(KV letters),': {
- '0': { action_: 'rm', nextState: '0' } },
- 'formula$': {
- '0': { nextState: 'f', revisit: true } },
- '1/2$': {
- '0': { action_: '1/2' } },
- 'else': {
- '0': { nextState: '!f', revisit: true } },
- '${(...)}$|$(...)$': {
- '*': { action_: 'tex-math' } },
- '{(...)}': {
- '*': { action_: 'text' } },
- 'a-z': {
- 'f': { action_: 'tex-math' } },
- 'letters': {
- '*': { action_: 'rm' } },
- '-9.,9': {
- '*': { action_: '9,9' } },
- ',': {
- '*': { action_: { type_: 'insert+p1', option: 'comma enumeration S' } } },
- '\\color{(...)}{(...)}1|\\color(...){(...)}2': {
- '*': { action_: 'color-output' } },
- '\\color{(...)}0': {
- '*': { action_: 'color0-output' } },
- '\\ce{(...)}': {
- '*': { action_: 'ce' } },
- '\\,|\\x{}{}|\\x{}|\\x': {
- '*': { action_: 'copy' } },
- 'else2': {
- '*': { action_: 'copy' } }
- }),
- actions: {
- 'state of aggregation': function (buffer, m) {
- return { type_: 'state of aggregation subscript', p1: mhchemParser.go(m, 'o') };
- },
- 'color-output': function (buffer, m) {
- return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'pq') };
- }
- }
- },
- 'bd': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': {} },
- 'x$': {
- '0': { nextState: '!f', revisit: true } },
- 'formula$': {
- '0': { nextState: 'f', revisit: true } },
- 'else': {
- '0': { nextState: '!f', revisit: true } },
- '-9.,9 no missing 0': {
- '*': { action_: '9,9' } },
- '.': {
- '*': { action_: { type_: 'insert', option: 'electron dot' } } },
- 'a-z': {
- 'f': { action_: 'tex-math' } },
- 'x': {
- '*': { action_: { type_: 'insert', option: 'KV x' } } },
- 'letters': {
- '*': { action_: 'rm' } },
- '\'': {
- '*': { action_: { type_: 'insert', option: 'prime' } } },
- '${(...)}$|$(...)$': {
- '*': { action_: 'tex-math' } },
- '{(...)}': {
- '*': { action_: 'text' } },
- '\\color{(...)}{(...)}1|\\color(...){(...)}2': {
- '*': { action_: 'color-output' } },
- '\\color{(...)}0': {
- '*': { action_: 'color0-output' } },
- '\\ce{(...)}': {
- '*': { action_: 'ce' } },
- '\\,|\\x{}{}|\\x{}|\\x': {
- '*': { action_: 'copy' } },
- 'else2': {
- '*': { action_: 'copy' } }
- }),
- actions: {
- 'color-output': function (buffer, m) {
- return { type_: 'color', color1: m[0], color2: mhchemParser.go(m[1], 'bd') };
- }
- }
- },
- 'oxidation': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': {} },
- 'roman numeral': {
- '*': { action_: 'roman-numeral' } },
- '${(...)}$|$(...)$': {
- '*': { action_: 'tex-math' } },
- 'else': {
- '*': { action_: 'copy' } }
- }),
- actions: {
- 'roman-numeral': function (buffer, m) { return { type_: 'roman numeral', p1: m || "" }; }
- }
- },
- 'tex-math': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': { action_: 'output' } },
- '\\ce{(...)}': {
- '*': { action_: [ 'output', 'ce' ] } },
- '{...}|\\,|\\x{}{}|\\x{}|\\x': {
- '*': { action_: 'o=' } },
- 'else': {
- '*': { action_: 'o=' } }
- }),
- actions: {
- 'output': function (buffer) {
- if (buffer.o) {
- /** @type {ParserOutput} */
- var ret = { type_: 'tex-math', p1: buffer.o };
- for (var p in buffer) { delete buffer[p]; }
- return ret;
- }
- }
- }
- },
- 'tex-math tight': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': { action_: 'output' } },
- '\\ce{(...)}': {
- '*': { action_: [ 'output', 'ce' ] } },
- '{...}|\\,|\\x{}{}|\\x{}|\\x': {
- '*': { action_: 'o=' } },
- '-|+': {
- '*': { action_: 'tight operator' } },
- 'else': {
- '*': { action_: 'o=' } }
- }),
- actions: {
- 'tight operator': function (buffer, m) { buffer.o = (buffer.o || "") + "{"+m+"}"; },
- 'output': function (buffer) {
- if (buffer.o) {
- /** @type {ParserOutput} */
- var ret = { type_: 'tex-math', p1: buffer.o };
- for (var p in buffer) { delete buffer[p]; }
- return ret;
- }
- }
- }
- },
- '9,9': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': {} },
- ',': {
- '*': { action_: 'comma' } },
- 'else': {
- '*': { action_: 'copy' } }
- }),
- actions: {
- 'comma': function () { return { type_: 'commaDecimal' }; }
- }
- },
- //#endregion
- //
- // \pu state machines
- //
- //#region pu
- 'pu': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': { action_: 'output' } },
- 'space$': {
- '*': { action_: [ 'output', 'space' ] } },
- '{[(|)]}': {
- '0|a': { action_: 'copy' } },
- '(-)(9)^(-9)': {
- '0': { action_: 'number^', nextState: 'a' } },
- '(-)(9.,9)(e)(99)': {
- '0': { action_: 'enumber', nextState: 'a' } },
- 'space': {
- '0|a': {} },
- 'pm-operator': {
- '0|a': { action_: { type_: 'operator', option: '\\pm' }, nextState: '0' } },
- 'operator': {
- '0|a': { action_: 'copy', nextState: '0' } },
- '//': {
- 'd': { action_: 'o=', nextState: '/' } },
- '/': {
- 'd': { action_: 'o=', nextState: '/' } },
- '{...}|else': {
- '0|d': { action_: 'd=', nextState: 'd' },
- 'a': { action_: [ 'space', 'd=' ], nextState: 'd' },
- '/|q': { action_: 'q=', nextState: 'q' } }
- }),
- actions: {
- 'enumber': function (buffer, m) {
- /** @type {ParserOutput[]} */
- var ret = [];
- if (m[0] === "+-" || m[0] === "+/-") {
- ret.push("\\pm ");
- } else if (m[0]) {
- ret.push(m[0]);
- }
- if (m[1]) {
- mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9'));
- if (m[2]) {
- if (m[2].match(/[,.]/)) {
- mhchemParser.concatArray(ret, mhchemParser.go(m[2], 'pu-9,9'));
- } else {
- ret.push(m[2]);
- }
- }
- m[3] = m[4] || m[3];
- if (m[3]) {
- m[3] = m[3].trim();
- if (m[3] === "e" || m[3].substr(0, 1) === "*") {
- ret.push({ type_: 'cdot' });
- } else {
- ret.push({ type_: 'times' });
- }
- }
- }
- if (m[3]) {
- ret.push("10^{"+m[5]+"}");
- }
- return ret;
- },
- 'number^': function (buffer, m) {
- /** @type {ParserOutput[]} */
- var ret = [];
- if (m[0] === "+-" || m[0] === "+/-") {
- ret.push("\\pm ");
- } else if (m[0]) {
- ret.push(m[0]);
- }
- mhchemParser.concatArray(ret, mhchemParser.go(m[1], 'pu-9,9'));
- ret.push("^{"+m[2]+"}");
- return ret;
- },
- 'operator': function (buffer, m, p1) { return { type_: 'operator', kind_: (p1 || m) }; },
- 'space': function () { return { type_: 'pu-space-1' }; },
- 'output': function (buffer) {
- /** @type {ParserOutput | ParserOutput[]} */
- var ret;
- var md = mhchemParser.patterns.match_('{(...)}', buffer.d || "");
- if (md && md.remainder === '') { buffer.d = md.match_; }
- var mq = mhchemParser.patterns.match_('{(...)}', buffer.q || "");
- if (mq && mq.remainder === '') { buffer.q = mq.match_; }
- if (buffer.d) {
- buffer.d = buffer.d.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
- buffer.d = buffer.d.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
- }
- if (buffer.q) { // fraction
- buffer.q = buffer.q.replace(/\u00B0C|\^oC|\^{o}C/g, "{}^{\\circ}C");
- buffer.q = buffer.q.replace(/\u00B0F|\^oF|\^{o}F/g, "{}^{\\circ}F");
- var b5 = {
- d: mhchemParser.go(buffer.d, 'pu'),
- q: mhchemParser.go(buffer.q, 'pu')
- };
- if (buffer.o === '//') {
- ret = { type_: 'pu-frac', p1: b5.d, p2: b5.q };
- } else {
- ret = b5.d;
- if (b5.d.length > 1 || b5.q.length > 1) {
- ret.push({ type_: ' / ' });
- } else {
- ret.push({ type_: '/' });
- }
- mhchemParser.concatArray(ret, b5.q);
- }
- } else { // no fraction
- ret = mhchemParser.go(buffer.d, 'pu-2');
- }
- for (var p in buffer) { delete buffer[p]; }
- return ret;
- }
- }
- },
- 'pu-2': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '*': { action_: 'output' } },
- '*': {
- '*': { action_: [ 'output', 'cdot' ], nextState: '0' } },
- '\\x': {
- '*': { action_: 'rm=' } },
- 'space': {
- '*': { action_: [ 'output', 'space' ], nextState: '0' } },
- '^{(...)}|^(-1)': {
- '1': { action_: '^(-1)' } },
- '-9.,9': {
- '0': { action_: 'rm=', nextState: '0' },
- '1': { action_: '^(-1)', nextState: '0' } },
- '{...}|else': {
- '*': { action_: 'rm=', nextState: '1' } }
- }),
- actions: {
- 'cdot': function () { return { type_: 'tight cdot' }; },
- '^(-1)': function (buffer, m) { buffer.rm += "^{"+m+"}"; },
- 'space': function () { return { type_: 'pu-space-2' }; },
- 'output': function (buffer) {
- /** @type {ParserOutput | ParserOutput[]} */
- var ret = [];
- if (buffer.rm) {
- var mrm = mhchemParser.patterns.match_('{(...)}', buffer.rm || "");
- if (mrm && mrm.remainder === '') {
- ret = mhchemParser.go(mrm.match_, 'pu');
- } else {
- ret = { type_: 'rm', p1: buffer.rm };
- }
- }
- for (var p in buffer) { delete buffer[p]; }
- return ret;
- }
- }
- },
- 'pu-9,9': {
- transitions: mhchemParser.createTransitions({
- 'empty': {
- '0': { action_: 'output-0' },
- 'o': { action_: 'output-o' } },
- ',': {
- '0': { action_: [ 'output-0', 'comma' ], nextState: 'o' } },
- '.': {
- '0': { action_: [ 'output-0', 'copy' ], nextState: 'o' } },
- 'else': {
- '*': { action_: 'text=' } }
- }),
- actions: {
- 'comma': function () { return { type_: 'commaDecimal' }; },
- 'output-0': function (buffer) {
- /** @type {ParserOutput[]} */
- var ret = [];
- buffer.text_ = buffer.text_ || "";
- if (buffer.text_.length > 4) {
- var a = buffer.text_.length % 3;
- if (a === 0) { a = 3; }
- for (var i=buffer.text_.length-3; i>0; i-=3) {
- ret.push(buffer.text_.substr(i, 3));
- ret.push({ type_: '1000 separator' });
- }
- ret.push(buffer.text_.substr(0, a));
- ret.reverse();
- } else {
- ret.push(buffer.text_);
- }
- for (var p in buffer) { delete buffer[p]; }
- return ret;
- },
- 'output-o': function (buffer) {
- /** @type {ParserOutput[]} */
- var ret = [];
- buffer.text_ = buffer.text_ || "";
- if (buffer.text_.length > 4) {
- var a = buffer.text_.length - 3;
- for (var i=0; i": return "rightarrow";
- case "\u2192": return "rightarrow";
- case "\u27F6": return "rightarrow";
- case "<-": return "leftarrow";
- case "<->": return "leftrightarrow";
- case "<-->": return "rightleftarrows";
- case "<=>": return "rightleftharpoons";
- case "\u21CC": return "rightleftharpoons";
- case "<=>>": return "rightequilibrium";
- case "<<=>": return "leftequilibrium";
- default:
- assertNever(a);
- throw ["MhchemBugT", "mhchem bug T. Please report."];
- }
- },
- _getBond: function (a) {
- switch (a) {
- case "-": return "{-}";
- case "1": return "{-}";
- case "=": return "{=}";
- case "2": return "{=}";
- case "#": return "{\\equiv}";
- case "3": return "{\\equiv}";
- case "~": return "{\\tripledash}";
- case "~-": return "{\\mathrlap{\\raisebox{-.1em}{$-$}}\\raisebox{.1em}{$\\tripledash$}}";
- case "~=": return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";
- case "~--": return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$\\tripledash$}}-}";
- case "-~-": return "{\\mathrlap{\\raisebox{-.2em}{$-$}}\\mathrlap{\\raisebox{.2em}{$-$}}\\tripledash}";
- case "...": return "{{\\cdot}{\\cdot}{\\cdot}}";
- case "....": return "{{\\cdot}{\\cdot}{\\cdot}{\\cdot}}";
- case "->": return "{\\rightarrow}";
- case "<-": return "{\\leftarrow}";
- case "<": return "{<}";
- case ">": return "{>}";
- default:
- assertNever(a);
- throw ["MhchemBugT", "mhchem bug T. Please report."];
- }
- },
- _getOperator: function (a) {
- switch (a) {
- case "+": return " {}+{} ";
- case "-": return " {}-{} ";
- case "=": return " {}={} ";
- case "<": return " {}<{} ";
- case ">": return " {}>{} ";
- case "<<": return " {}\\ll{} ";
- case ">>": return " {}\\gg{} ";
- case "\\pm": return " {}\\pm{} ";
- case "\\approx": return " {}\\approx{} ";
- case "$\\approx$": return " {}\\approx{} ";
- case "v": return " \\downarrow{} ";
- case "(v)": return " \\downarrow{} ";
- case "^": return " \\uparrow{} ";
- case "(^)": return " \\uparrow{} ";
- default:
- assertNever(a);
- throw ["MhchemBugT", "mhchem bug T. Please report."];
- }
- }
- };
-
- //
- // Helpers for code anaylsis
- // Will show type error at calling position
- //
- /** @param {number} a */
- function assertNever(a) {}
- /** @param {string} a */
- function assertString(a) {}
-
- // ********************************************************************************
- // ********************************************************************************
- // End of paste https://github.com/KaTeX/KaTeX/blob/master/contrib/mhchem/mhchem.js
- // ********************************************************************************
- // ********************************************************************************
-
- return katex;
-}
-
-if (this.katex) {
- // We're running in a browser and the global Katex variable is defined
- this.katex = mhchemModule(this.katex);
-} else if (typeof module !== 'undefined' && module.exports) {
- // We're running in Node.js
- module.exports = mhchemModule;
-}
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/rules/link_open.js b/ReactNativeClient/lib/renderers/MdToHtml/rules/link_open.js
deleted file mode 100644
index 2fbcf4ea9..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/rules/link_open.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const Entities = require('html-entities').AllHtmlEntities;
-const htmlentities = new Entities().encode;
-const utils = require('../../utils');
-const urlUtils = require('lib/urlUtils.js');
-const { getClassNameForMimeType } = require('font-awesome-filetypes');
-
-function installRule(markdownIt, mdOptions, ruleOptions) {
- markdownIt.renderer.rules.link_open = function(tokens, idx) {
- const token = tokens[idx];
- let href = utils.getAttr(token.attrs, 'href');
- const resourceHrefInfo = urlUtils.parseResourceUrl(href);
- const isResourceUrl = !!resourceHrefInfo;
- let title = utils.getAttr(token.attrs, 'title', isResourceUrl ? '' : href);
-
- let resourceIdAttr = '';
- let icon = '';
- let hrefAttr = '#';
- let mime = '';
- if (isResourceUrl) {
- const resourceId = resourceHrefInfo.itemId;
-
- const result = ruleOptions.resources[resourceId];
- const resourceStatus = utils.resourceStatus(result);
-
- if (result && result.item) {
- title = utils.getAttr(token.attrs, 'title', result.item.title);
- mime = result.item.mime;
- }
-
- if (result && resourceStatus !== 'ready' && !ruleOptions.plainResourceRendering) {
- const icon = utils.resourceStatusFile(resourceStatus);
- return ` ` + ` `;
- } else {
- href = `joplin://${resourceId}`;
- if (resourceHrefInfo.hash) href += `#${resourceHrefInfo.hash}`;
- resourceIdAttr = `data-resource-id='${resourceId}'`;
-
- let iconType = getClassNameForMimeType(mime);
- if (!mime) {
- iconType = 'fa-joplin';
- }
- // Icons are defined in lib/renderers/noteStyle.js using inline svg
- // The icons are taken from fork-awesome but use the font-awesome naming scheme in order
- // to be more compatible with the getClass library
- icon = ` `;
- }
- } else {
- // If the link is a plain URL (as opposed to a resource link), set the href to the actual
- // link. This allows the link to be exported too when exporting to PDF.
- hrefAttr = href;
- }
-
- // A single quote is valid in a URL but we don't want any because the
- // href is already enclosed in single quotes.
- // https://github.com/laurent22/joplin/issues/2030
- href = href.replace(/'/g, '%27');
-
- let js = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)}); return false;`;
- if (hrefAttr.indexOf('#') === 0 && href.indexOf('#') === 0) js = ''; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place
-
- if (ruleOptions.plainResourceRendering) {
- return ` `;
- } else {
- return ` ${icon}`;
- }
- };
-}
-
-module.exports = function(context, ruleOptions) {
-
- return function(md, mdOptions) {
- installRule(md, mdOptions, ruleOptions);
- };
-};
diff --git a/ReactNativeClient/lib/renderers/MdToHtml/setupLinkify.js b/ReactNativeClient/lib/renderers/MdToHtml/setupLinkify.js
deleted file mode 100644
index 5552f8f45..000000000
--- a/ReactNativeClient/lib/renderers/MdToHtml/setupLinkify.js
+++ /dev/null
@@ -1,29 +0,0 @@
-module.exports = function(markdownIt) {
- // Add `file:` protocol in linkify to allow text in the format of "file://..." to translate into
- // file-URL links in html view
- markdownIt.linkify.add('file:', {
- validate: function(text, pos, self) {
- var tail = text.slice(pos);
- if (!self.re.file) {
- // matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
- self.re.file = new RegExp('^[\\/]{2,3}[\\S]+');
- }
- if (self.re.file.test(tail)) {
- return tail.match(self.re.file)[0].length;
- }
- return 0;
- },
- });
-
- // enable file link URLs in MarkdownIt. Keeps other URL restrictions of MarkdownIt untouched.
- // Format [link name](file://...)
- markdownIt.validateLink = function(url) {
- var BAD_PROTO_RE = /^(vbscript|javascript|data):/;
- var GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
-
- // url should be normalized at this point, and existing entities are decoded
- var str = url.trim().toLowerCase();
-
- return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
- };
-};
diff --git a/ReactNativeClient/lib/renderers/noteStyle.js b/ReactNativeClient/lib/renderers/noteStyle.js
deleted file mode 100644
index 64d4d5764..000000000
--- a/ReactNativeClient/lib/renderers/noteStyle.js
+++ /dev/null
@@ -1,291 +0,0 @@
-module.exports = function(style, options) {
- // https://necolas.github.io/normalize.css/
- const normalizeCss = `
- html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
- article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
- pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
- b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
- `;
-
- const fontFamily = '\'Avenir\', \'Arial\', sans-serif';
-
- const css =
- `
- body {
- font-size: ${style.htmlFontSize};
- color: ${style.htmlColor};
- line-height: ${style.htmlLineHeight};
- background-color: ${style.htmlBackgroundColor};
- font-family: ${fontFamily};
- padding-bottom: ${options.paddingBottom};
- }
- strong {
- color: ${style.colorBright};
- }
- kbd {
- border: 1px solid ${style.htmlCodeBorderColor};
- box-shadow: inset 0 -1px 0 ${style.htmlCodeBorderColor};
- padding: 2px 4px;
- border-radius: 3px;
- background-color: ${style.htmlCodeBackgroundColor};
- }
- ::-webkit-scrollbar {
- width: 7px;
- height: 7px;
- }
- ::-webkit-scrollbar-corner {
- background: none;
- }
- ::-webkit-scrollbar-track {
- border: none;
- }
- ::-webkit-scrollbar-thumb {
- background: rgba(100, 100, 100, 0.3);
- border-radius: 5px;
- }
- ::-webkit-scrollbar-track:hover {
- background: rgba(0, 0, 0, 0.1);
- }
- ::-webkit-scrollbar-thumb:hover {
- background: rgba(100, 100, 100, 0.7);
- }
-
- /* Remove top padding and margin from first child so that top of rendered text is aligned to top of text editor text */
- #rendered-md h1:first-child,
- #rendered-md h2:first-child,
- #rendered-md h3:first-child,
- #rendered-md h4:first-child,
- #rendered-md ul:first-child,
- #rendered-md ol:first-child,
- #rendered-md table:first-child,
- #rendered-md blockquote:first-child,
- #rendered-md img:first-child,
- #rendered-md p:first-child {
- margin-top: 0;
- padding-top: 0;
- }
-
- p, h1, h2, h3, h4, h5, h6, ul, table {
- margin-top: .6em;
- margin-bottom: .65em;
- }
- h1, h2, h3, h4, h5, h6 {
- line-height: 1.5em;
- }
- h1 {
- font-size: 1.5em;
- font-weight: bold;
- border-bottom: 1px solid ${style.htmlDividerColor};
- padding-bottom: .3em;
- }
- h2 {
- font-size: 1.3em;
- font-weight: bold;
- padding-bottom: .1em; */
- }
- h3 {
- font-size: 1.1em;
- }
- h4, h5, h6 {
- font-size: 1em;
- font-weight: bold;
- }
- a {
- color: ${style.htmlLinkColor};
- }
- ul, ol {
- padding-left: 0;
- margin-left: 1.7em;
- }
- li {
- margin-bottom: .4em;
- }
- li p {
- margin-top: 0.2em;
- margin-bottom: 0;
- }
- .resource-icon {
- display: inline-block;
- position: relative;
- top: .5em;
- text-decoration: none;
- width: 1.2em;
- height: 1.4em;
- margin-right: 0.4em;
- background-color: ${style.htmlLinkColor};
- }
- /* These icons are obtained from the wonderful ForkAwesome project by copying the src svgs
- * into the css classes below.
- * svgs are obtained from https://github.com/ForkAwesome/Fork-Awesome/tree/master/src/icons/svg
- * instead of the svg width, height property you must use a viewbox here, 0 0 1536 1792 is typically the actual size of the icon
- * each line begins with the pre-amble -webkit-mask: url("data:image/svg+xml;utf8,
- * and of course finishes with ");
- * to precvent artifacts it is also necessary to include -webkit-mask-repeat: no-repeat;
- * on the following line
- * */
- .fa-joplin {
- /* Awesome Font file */
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- }
- .fa-file-image {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-pdf {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-word {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-powerpoint {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-excel {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-audio {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-video {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-archive {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-code {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file-alt, .fa-file-csv {
- /* fork-awesome doesn't have csv so we use the text icon */
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- .fa-file {
- -webkit-mask: url("data:image/svg+xml;utf8, ");
- -webkit-mask-repeat: no-repeat;
- }
- blockquote {
- border-left: 4px solid ${style.htmlCodeBorderColor};
- padding-left: 1.2em;
- margin-left: 0;
- opacity: .7;
- }
- table {
- text-align: left-align;
- border-collapse: collapse;
- border: 1px solid ${style.htmlCodeBorderColor};
- background-color: ${style.htmlBackgroundColor};
- }
- td, th {
- padding: .5em 1em .5em 1em;
- font-size: ${style.htmlFontSize};
- color: ${style.htmlColor};
- font-family: ${fontFamily};
- }
- td {
- border: 1px solid ${style.htmlCodeBorderColor};
- }
- th {
- border: 1px solid ${style.htmlCodeBorderColor};
- border-bottom: 2px solid ${style.htmlCodeBorderColor};
- background-color: ${style.htmlTableBackgroundColor};
- }
- tr:nth-child(even) {
- background-color: ${style.htmlTableBackgroundColor};
- }
- tr:hover {
- background-color: ${style.raisedBackgroundColor};
- }
- hr {
- border: none;
- border-bottom: 2px solid ${style.htmlDividerColor};
- }
- img {
- max-width: 100%;
- height: auto;
- }
- .inline-code {
- border: 1px solid ${style.htmlCodeBorderColor};
- background-color: ${style.htmlCodeBackgroundColor};
- padding-right: .2em;
- padding-left: .2em;
- border-radius: .25em;
- color: ${style.htmlCodeColor};
- font-size: ${style.htmlCodeFontSize};
- }
-
- .highlighted-keyword {
- background-color: #F3B717;
- color: black;
- }
-
- .not-loaded-resource img {
- width: 1.15em;
- height: 1.15em;
- background: white;
- padding: 2px !important;
- border-radius: 2px;
- box-shadow: 0 1px 3px #000000aa;
- }
-
- a.not-loaded-resource img {
- margin-right: .2em;
- }
-
- a.not-loaded-resource {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
-
- .md-checkbox input[type=checkbox]:checked {
- opacity: 0.7;
- }
-
- .md-checkbox .checkbox-label-checked {
- opacity: 0.5;
- }
-
- .exported-note-title {
- font-size: 2.2em;
- font-weight: bold;
- margin-bottom: 1em;
- }
-
- .exported-note {
- padding: 1em;
- }
-
- @media print {
- body {
- height: auto !important;
- }
-
- pre {
- white-space: pre-wrap;
- }
-
- .code, .inline-code {
- border: 1px solid #CBCBCB;
- }
-
- #joplin-container-content {
- /* The height of the content is set dynamically by JavaScript (in updateBodyHeight) to go
- around various issues related to scrolling. However when printing we don't want this
- fixed size as that would crop the content. So we set it to auto here. "important" is
- needed to override the style set by JavaScript at the element-level. */
- height: auto !important;
- }
- }
- `;
-
- return [normalizeCss, css];
-};
diff --git a/ReactNativeClient/lib/renderers/utils.js b/ReactNativeClient/lib/renderers/utils.js
deleted file mode 100644
index 10ff06b0f..000000000
--- a/ReactNativeClient/lib/renderers/utils.js
+++ /dev/null
@@ -1,122 +0,0 @@
-const Resource = require('lib/models/Resource.js');
-const Entities = require('html-entities').AllHtmlEntities;
-const htmlentities = new Entities().encode;
-
-const utils = {};
-
-utils.getAttr = function(attrs, name, defaultValue = null) {
- for (let i = 0; i < attrs.length; i++) {
- if (attrs[i][0] === name) return attrs[i].length > 1 ? attrs[i][1] : null;
- }
- return defaultValue;
-};
-
-utils.notDownloadedResource = function() {
- return `
-
-
-
- `;
-};
-
-utils.notDownloadedImage = function() {
- // https://github.com/ForkAwesome/Fork-Awesome/blob/master/src/icons/svg/file-image-o.svg
- // Height changed to 1795
- return `
-
-
-
- `;
-};
-
-utils.notDownloadedFile = function() {
- // https://github.com/ForkAwesome/Fork-Awesome/blob/master/src/icons/svg/file-o.svg
- return `
-
-
-
- `;
-};
-
-utils.errorImage = function() {
- // https://github.com/ForkAwesome/Fork-Awesome/blob/master/src/icons/svg/times-circle.svg
- return `
-
-
-
- `;
-};
-
-utils.loaderImage = function() {
- // https://github.com/ForkAwesome/Fork-Awesome/blob/master/src/icons/svg/hourglass-half.svg
- return `
-
-
-
- `;
-};
-
-utils.resourceStatusImage = function(state) {
- if (state === 'notDownloaded') return utils.notDownloadedResource();
- return utils.resourceStatusFile(state);
-};
-
-utils.resourceStatusFile = function(state) {
- if (state === 'notDownloaded') return utils.notDownloadedResource();
- if (state === 'downloading') return utils.loaderImage();
- if (state === 'encrypted') return utils.loaderImage();
- if (state === 'error') return utils.errorImage();
-
- throw new Error(`Unknown state: ${state}`);
-};
-
-utils.resourceStatus = function(resourceInfo) {
- let resourceStatus = 'ready';
-
- if (resourceInfo) {
- const resource = resourceInfo.item;
- const localState = resourceInfo.localState;
-
- if (localState.fetch_status === Resource.FETCH_STATUS_IDLE) {
- resourceStatus = 'notDownloaded';
- } else if (localState.fetch_status === Resource.FETCH_STATUS_STARTED) {
- resourceStatus = 'downloading';
- } else if (localState.fetch_status === Resource.FETCH_STATUS_DONE) {
- if (resource.encryption_blob_encrypted || resource.encryption_applied) {
- resourceStatus = 'encrypted';
- }
- }
- } else {
- resourceStatus = 'notDownloaded';
- }
-
- return resourceStatus;
-};
-
-utils.imageReplacement = function(src, resources, resourceBaseUrl) {
- if (!Resource.isResourceUrl(src)) return null;
-
- const resourceId = Resource.urlToId(src);
- const result = resources[resourceId];
- const resource = result ? result.item : null;
- const resourceStatus = utils.resourceStatus(result);
-
- if (resourceStatus !== 'ready') {
- const icon = utils.resourceStatusImage(resourceStatus);
- return `` + `
` + '
';
- }
-
- const mime = resource.mime ? resource.mime.toLowerCase() : '';
- if (Resource.isSupportedImageMimeType(mime)) {
- let newSrc = `./${Resource.filename(resource)}`;
- if (resourceBaseUrl) newSrc = resourceBaseUrl + newSrc;
- return {
- 'data-resource-id': resource.id,
- src: newSrc,
- };
- }
-
- return null;
-};
-
-module.exports = utils;
diff --git a/ReactNativeClient/lib/services/InteropService_Exporter_Html.js b/ReactNativeClient/lib/services/InteropService_Exporter_Html.js
index 6fdb78ca4..3f8db2a02 100644
--- a/ReactNativeClient/lib/services/InteropService_Exporter_Html.js
+++ b/ReactNativeClient/lib/services/InteropService_Exporter_Html.js
@@ -6,11 +6,11 @@ const Note = require('lib/models/Note');
const Setting = require('lib/models/Setting');
const Resource = require('lib/models/Resource');
const { shim } = require('lib/shim');
-const MarkupToHtml = require('lib/renderers/MarkupToHtml.js');
const dataurl = require('dataurl');
const { themeStyle } = require('../../theme.js');
const { dirname } = require('lib/path-utils.js');
const { escapeHtml } = require('lib/string-utils.js');
+const markupLanguageUtils = require('lib/markupLanguageUtils');
class InteropService_Exporter_Html extends InteropService_Exporter_Base {
@@ -27,7 +27,7 @@ class InteropService_Exporter_Html extends InteropService_Exporter_Base {
this.resourceDir_ = this.destDir_ ? `${this.destDir_}/_resources` : null;
await shim.fsDriver().mkdir(this.destDir_);
- this.markupToHtml_ = new MarkupToHtml();
+ this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
this.resources_ = [];
this.style_ = themeStyle(Setting.THEME_LIGHT);
}
@@ -103,7 +103,7 @@ class InteropService_Exporter_Html extends InteropService_Exporter_Base {
}
const bodyMd = await this.processNoteResources_(item);
- const result = this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, { resources: this.resources_, plainResourceRendering: true });
+ const result = await this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, { resources: this.resources_, plainResourceRendering: true });
const noteContent = [];
if (item.title) noteContent.push(`${escapeHtml(item.title)}
`);
if (result.html) noteContent.push(result.html);
diff --git a/ReactNativeClient/lib/services/rest/Api.js b/ReactNativeClient/lib/services/rest/Api.js
index 10c883c97..c2dd92d49 100644
--- a/ReactNativeClient/lib/services/rest/Api.js
+++ b/ReactNativeClient/lib/services/rest/Api.js
@@ -22,6 +22,7 @@ const ApiResponse = require('lib/services/rest/ApiResponse');
const SearchEngineUtils = require('lib/services/SearchEngineUtils');
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
const uri2path = require('file-uri-to-path');
+const { MarkupToHtml } = require('joplin-renderer');
class ApiError extends Error {
constructor(message, httpCode = 400) {
@@ -491,7 +492,7 @@ class Api {
}
output.body = styleTag + minifiedHtml;
output.body = htmlUtils.prependBaseUrl(output.body, baseUrl);
- output.markup_language = Note.MARKUP_LANGUAGE_HTML;
+ output.markup_language = MarkupToHtml.MARKUP_LANGUAGE_HTML;
} else {
// Convert to Markdown
// Parsing will not work if the HTML is not wrapped in a top level tag, which is not guaranteed
@@ -501,7 +502,7 @@ class Api {
baseUrl: baseUrl,
anchorNames: requestNote.anchor_names ? requestNote.anchor_names : [],
});
- output.markup_language = Note.MARKUP_LANGUAGE_MARKDOWN;
+ output.markup_language = MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
}
}
@@ -520,7 +521,7 @@ class Api {
if ('is_todo' in requestNote) output.is_todo = Database.formatValue(Database.TYPE_INT, requestNote.is_todo);
if ('markup_language' in requestNote) output.markup_language = Database.formatValue(Database.TYPE_INT, requestNote.markup_language);
- if (!output.markup_language) output.markup_language = Note.MARKUP_LANGUAGE_MARKDOWN;
+ if (!output.markup_language) output.markup_language = MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN;
return output;
}
@@ -664,7 +665,7 @@ class Api {
replaceImageUrlsByResources_(markupLanguage, md, urls, imageSizes) {
const imageSizesIndexes = {};
- if (markupLanguage === Note.MARKUP_LANGUAGE_HTML) {
+ if (markupLanguage === MarkupToHtml.MARKUP_LANGUAGE_HTML) {
return htmlUtils.replaceImageUrls(md, imageUrl => {
const urlInfo = urls[imageUrl];
if (!urlInfo || !urlInfo.resource) return imageUrl;
diff --git a/ReactNativeClient/lib/shim-init-react.js b/ReactNativeClient/lib/shim-init-react.js
index 3dd56c1ff..f701560b7 100644
--- a/ReactNativeClient/lib/shim-init-react.js
+++ b/ReactNativeClient/lib/shim-init-react.js
@@ -16,11 +16,6 @@ const injectedJs = {
webviewLib: require('lib/rnInjectedJs/webviewLib'),
};
-const cssToJs = {
- 'hljs-atom-one-dark-reasonable.css': require('lib/csstojs/hljs-atom-one-dark-reasonable.css.js'),
- 'hljs-atom-one-light.css': require('lib/csstojs/hljs-atom-one-light.css.js'),
-};
-
function shimInit() {
shim.Geolocation = GeolocationReact;
shim.setInterval = PoorManIntervals.setInterval;
@@ -187,11 +182,6 @@ function shimInit() {
if (!(name in injectedJs)) throw new Error(`Cannot find injectedJs file (add it to "injectedJs" object): ${name}`);
return injectedJs[name];
};
-
- shim.loadCssFromJs = function(name) {
- if (!(name in cssToJs)) throw new Error(`Cannot find csstojs file (add it to "cssToJs" object): ${name}`);
- return cssToJs[name];
- };
}
module.exports = { shimInit };
diff --git a/ReactNativeClient/lib/shim.js b/ReactNativeClient/lib/shim.js
index e26aae053..fa230c601 100644
--- a/ReactNativeClient/lib/shim.js
+++ b/ReactNativeClient/lib/shim.js
@@ -199,10 +199,6 @@ shim.waitForFrame = () => {
shim.injectedJs = name => '';
-shim.loadCssFromJs = name => {
- throw new Error('Not implemented');
-};
-
let isTestingEnv_ = false;
shim.isTestingEnv = () => {
diff --git a/ReactNativeClient/package-lock.json b/ReactNativeClient/package-lock.json
index 2a0485857..c57a04012 100644
--- a/ReactNativeClient/package-lock.json
+++ b/ReactNativeClient/package-lock.json
@@ -1466,6 +1466,12 @@
}
}
},
+ "app-module-path": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz",
+ "integrity": "sha1-ZBqlXft9am8KgUHEucCqULbCTdU=",
+ "dev": true
+ },
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
@@ -2727,22 +2733,19 @@
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"fs-extra": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
- "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"requires": {
- "graceful-fs": "^4.1.2",
- "jsonfile": "^2.1.0",
- "klaw": "^1.0.0"
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
},
"dependencies": {
- "jsonfile": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
- "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
- "requires": {
- "graceful-fs": "^4.1.6"
- }
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
}
}
},
@@ -2768,8 +2771,7 @@
},
"ansi-regex": {
"version": "2.1.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"aproba": {
"version": "1.2.0",
@@ -2787,13 +2789,11 @@
},
"balanced-match": {
"version": "1.0.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2806,18 +2806,15 @@
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"concat-map": {
"version": "0.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"console-control-strings": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"core-util-is": {
"version": "1.0.2",
@@ -2920,8 +2917,7 @@
},
"inherits": {
"version": "2.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"ini": {
"version": "1.3.5",
@@ -2931,7 +2927,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -2944,20 +2939,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -2974,7 +2966,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -3047,8 +3038,7 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"object-assign": {
"version": "4.1.1",
@@ -3058,7 +3048,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -3134,8 +3123,7 @@
},
"safe-buffer": {
"version": "5.1.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -3165,7 +3153,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -3183,7 +3170,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -3222,13 +3208,11 @@
},
"wrappy": {
"version": "1.0.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"yallist": {
"version": "3.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
}
}
},
@@ -3313,6 +3297,40 @@
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
},
+ "handlebars": {
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
+ "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
+ "requires": {
+ "neo-async": "^2.6.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "optional": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "uglify-js": {
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.3.tgz",
+ "integrity": "sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==",
+ "optional": true,
+ "requires": {
+ "commander": "~2.20.3",
+ "source-map": "~0.6.1"
+ }
+ }
+ }
+ },
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@@ -3386,9 +3404,12 @@
}
},
"highlight.js": {
- "version": "9.15.6",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz",
- "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ=="
+ "version": "9.17.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.17.1.tgz",
+ "integrity": "sha512-TA2/doAur5Ol8+iM3Ov7qy3jYcr/QiJ2eDTdRF4dfbjG7AaaB99J5G+zSl11ljbl6cIcahgPY6SKb3sC3EJ0fw==",
+ "requires": {
+ "handlebars": "^4.5.3"
+ }
},
"hoist-non-react-statics": {
"version": "2.5.0",
@@ -3774,6 +3795,57 @@
"integrity": "sha512-+f/4OLeqY8RAmXnonI1ffeY1DR8kMNJPhv5WMFehchf7U71cjMQVKkOz1n6asz6kfVoAqKNWJz1A/18i18AcXA==",
"dev": true
},
+ "joplin-renderer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/joplin-renderer/-/joplin-renderer-1.0.4.tgz",
+ "integrity": "sha512-Q7GoEYA6hYtv8ZQgxccn/gSw25V8kQPyWdolK0PJhG7m2BJa0zunyagtaOhMkYuWl8beDYHwTsvsVx2ayKqMcQ==",
+ "requires": {
+ "base-64": "^0.1.0",
+ "font-awesome-filetypes": "^2.1.0",
+ "fs-extra": "^8.1.0",
+ "highlight.js": "^9.17.1",
+ "html-entities": "^1.2.1",
+ "katex": "^0.11.1",
+ "markdown-it": "^10.0.0",
+ "markdown-it-abbr": "^1.0.4",
+ "markdown-it-anchor": "^5.2.5",
+ "markdown-it-deflist": "^2.0.3",
+ "markdown-it-emoji": "^1.4.0",
+ "markdown-it-footnote": "^3.0.2",
+ "markdown-it-ins": "^3.0.0",
+ "markdown-it-mark": "^3.0.0",
+ "markdown-it-multimd-table": "^4.0.0",
+ "markdown-it-sub": "^1.0.0",
+ "markdown-it-sup": "^1.0.0",
+ "markdown-it-toc-done-right": "^4.1.0",
+ "md5": "^2.2.1",
+ "uslug": "^1.0.4"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+ "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
+ },
+ "markdown-it": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
+ "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
+ "requires": {
+ "argparse": "^1.0.7",
+ "entities": "~2.0.0",
+ "linkify-it": "^2.0.0",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ },
+ "uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+ }
+ }
+ },
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
@@ -4028,37 +4100,19 @@
"integrity": "sha512-JVW6fCmZWjvMdDQSbOT3nnOQtd9iAXmw7hTSh26+v42BnvXeVyGMDBm5b/EZocMed2MbCAHiTX632vY0FyGB8A=="
},
"markdown-it-ins": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-2.0.0.tgz",
- "integrity": "sha1-papqMPHi9x6Ul1Z8/f9A8f3mdIM="
- },
- "markdown-it-katex": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/markdown-it-katex/-/markdown-it-katex-2.0.3.tgz",
- "integrity": "sha1-17hqGuoLnWSW+rTnkZoY/e9YnDk=",
- "requires": {
- "katex": "^0.6.0"
- },
- "dependencies": {
- "katex": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/katex/-/katex-0.6.0.tgz",
- "integrity": "sha1-EkGOCRIcBckgQbazuftrqyE8tvM=",
- "requires": {
- "match-at": "^0.1.0"
- }
- }
- }
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-3.0.0.tgz",
+ "integrity": "sha512-+vyAdBuMGwmT2yMlAFJSx2VR/0QZ1onQ/Mkkmr4l9tDFOh5sVoAgRbkgbuSsk+sxJ9vaMH/IQ323ydfvQrPO/Q=="
},
"markdown-it-mark": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-2.0.0.tgz",
- "integrity": "sha1-RqGqlHEFrtgYiXjgoBYXnkBPQsc="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-3.0.0.tgz",
+ "integrity": "sha512-HqMWeKfMMOu4zBO0emmxsoMWmbf2cPKZY1wP6FsTbKmicFfp5y4L3KXAsNeO1rM6NTJVOrNlLKMPjWzriBGspw=="
},
"markdown-it-multimd-table": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/markdown-it-multimd-table/-/markdown-it-multimd-table-3.2.0.tgz",
- "integrity": "sha512-dBtYHVtyAnoWslzYLYDh0d9jX2/qroyMIsJ1BjFdIcLgZjzqBIBGwZ1j3AaaWh+IIfe3KVm5Irqn5Uzadm5kQA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-multimd-table/-/markdown-it-multimd-table-4.0.0.tgz",
+ "integrity": "sha512-kdM3fH+/sRMfHQgD2CM1BcIpLNODUCuoiFr6TwS7mDJBYntVXDJxZbFwGDRflIc9ZzAfsUbr0lnHc6RbYafIsw==",
"requires": {
"markdown-it": "^8.4.2"
},
@@ -4097,11 +4151,6 @@
"resolved": "https://registry.npmjs.org/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.1.0.tgz",
"integrity": "sha512-UhD2Oj6cZV3ycYPoelt4hTkwKIK3zbPP1wjjdpCq7UGtWQOFalDFDv1s2zBYV6aR2gMs/X8kpJcOYsQmUbiXDw=="
},
- "match-at": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/match-at/-/match-at-0.1.1.tgz",
- "integrity": "sha512-h4Yd392z9mST+dzc+yjuybOGFNOZjmXIPKWjxBd1Bb23r4SmDOsk2NYCU2BMUBGbSpZqwVsZYNq26QS3xfaT3Q=="
- },
"math-random": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",
@@ -4239,6 +4288,16 @@
"ua-parser-js": "^0.7.18"
}
},
+ "fs-extra": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
+ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0"
+ }
+ },
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -4247,6 +4306,14 @@
"loose-envify": "^1.0.0"
}
},
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"metro-react-native-babel-preset": {
"version": "0.51.1",
"resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.51.1.tgz",
@@ -4731,6 +4798,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
+ "neo-async": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
+ },
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -5352,6 +5424,11 @@
}
}
},
+ "react-async": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/react-async/-/react-async-10.0.0.tgz",
+ "integrity": "sha512-Uk7yG49ftaJ1ShMfgTqsZUcEIRxdWHV2ABojw+dzFifCSMeqydCFhzj9x7okACClCzMDgkGqC5/bOtY95yQG1A=="
+ },
"react-clone-referenced-element": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/react-clone-referenced-element/-/react-clone-referenced-element-1.1.0.tgz",
@@ -5488,6 +5565,16 @@
"ua-parser-js": "^0.7.18"
}
},
+ "fs-extra": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
+ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0"
+ }
+ },
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -5496,6 +5583,14 @@
"loose-envify": "^1.0.0"
}
},
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
diff --git a/ReactNativeClient/package.json b/ReactNativeClient/package.json
index 20c984523..e1a575a6a 100644
--- a/ReactNativeClient/package.json
+++ b/ReactNativeClient/package.json
@@ -6,7 +6,8 @@
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
- "postinstall": "node ../Tools/copycss.js && node ../Tools/buildReactNativeInjectedJs.js && npx jetify"
+ "postinstall": "node ../Tools/buildReactNativeInjectedJs.js && npx jetify && npm run encodeAssets",
+ "encodeAssets": "node encodeAssets.js"
},
"dependencies": {
"@react-native-community/slider": "^1.1.3",
@@ -16,31 +17,18 @@
"diacritics": "^1.3.0",
"diff-match-patch": "^1.0.4",
"events": "^1.1.1",
- "font-awesome-filetypes": "^2.1.0",
"form-data": "^2.1.4",
- "highlight.js": "^9.15.6",
"html-entities": "^1.2.1",
+ "joplin-renderer": "^1.0.4",
"jsc-android": "241213.1.0",
- "katex": "^0.11.1",
"markdown-it": "^8.4.0",
- "markdown-it-abbr": "^1.0.4",
- "markdown-it-anchor": "^5.2.5",
- "markdown-it-deflist": "^2.0.3",
- "markdown-it-emoji": "^1.4.0",
- "markdown-it-footnote": "^3.0.2",
- "markdown-it-ins": "^2.0.0",
- "markdown-it-katex": "^2.0.3",
- "markdown-it-mark": "^2.0.0",
- "markdown-it-multimd-table": "^3.2.0",
- "markdown-it-sub": "^1.0.0",
- "markdown-it-sup": "^1.0.0",
- "markdown-it-toc-done-right": "^4.1.0",
"md5": "^2.2.1",
"moment": "^2.24.0",
"prop-types": "^15.6.0",
"punycode": "^2.1.1",
"query-string": "4.3.4",
"react": "^16.8.3",
+ "react-async": "^10.0.0",
"react-native": "0.59.10",
"react-native-action-button": "^2.6.9",
"react-native-camera": "^2.10.2",
@@ -74,7 +62,6 @@
"timers": "^0.1.1",
"url": "^0.11.0",
"url-parse": "^1.4.7",
- "uslug": "^1.0.4",
"uuid": "^3.0.1",
"valid-url": "^1.0.9",
"word-wrap": "^1.2.3",
@@ -83,6 +70,8 @@
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/runtime": "^7.4.5",
+ "app-module-path": "^2.2.0",
+ "fs-extra": "^8.1.0",
"jetifier": "^1.6.4",
"metro-react-native-babel-preset": "^0.54.1",
"react-test-renderer": "^16.8.3"
diff --git a/ReactNativeClient/pluginAssets/highlight.js/atom-one-dark-reasonable.css.base64.js b/ReactNativeClient/pluginAssets/highlight.js/atom-one-dark-reasonable.css.base64.js
new file mode 100644
index 000000000..e7a0b3f63
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/highlight.js/atom-one-dark-reasonable.css.base64.js
@@ -0,0 +1 @@
+module.exports = `LyoKCkF0b20gT25lIERhcmsgV2l0aCBzdXBwb3J0IGZvciBSZWFzb25NTCBieSBHaWRpIE1vcnJpcywgYmFzZWQgb2ZmIHdvcmsgYnkgRGFuaWVsIEdhbWFnZQoKT3JpZ2luYWwgT25lIERhcmsgU3ludGF4IHRoZW1lIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2F0b20vb25lLWRhcmstc3ludGF4CgoqLwouaGxqcyB7CiAgZGlzcGxheTogYmxvY2s7CiAgb3ZlcmZsb3cteDogYXV0bzsKICBwYWRkaW5nOiAwLjVlbTsKICBjb2xvcjogI2FiYjJiZjsKICBiYWNrZ3JvdW5kOiAjMjgyYzM0Owp9Ci5obGpzLWtleXdvcmQsIC5obGpzLW9wZXJhdG9yIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIHsKICBjb2xvcjogI0Y5MjY3MjsKfQouaGxqcy1wYXR0ZXJuLW1hdGNoIC5obGpzLWNvbnN0cnVjdG9yIHsKICBjb2xvcjogIzYxYWVlZTsKfQouaGxqcy1mdW5jdGlvbiB7CiAgY29sb3I6ICM2MWFlZWU7Cn0KLmhsanMtZnVuY3Rpb24gLmhsanMtcGFyYW1zIHsKICBjb2xvcjogI0E2RTIyRTsKfQouaGxqcy1mdW5jdGlvbiAuaGxqcy1wYXJhbXMgLmhsanMtdHlwaW5nIHsKICBjb2xvcjogI0ZEOTcxRjsKfQouaGxqcy1tb2R1bGUtYWNjZXNzIC5obGpzLW1vZHVsZSB7CiAgY29sb3I6ICM3ZTU3YzI7Cn0KLmhsanMtY29uc3RydWN0b3IgewogIGNvbG9yOiAjZTJiOTNkOwp9Ci5obGpzLWNvbnN0cnVjdG9yIC5obGpzLXN0cmluZyB7CiAgY29sb3I6ICM5Q0NDNjU7Cn0KLmhsanMtY29tbWVudCwgLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYjE4ZWIxOwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQouaGxqcy1kb2N0YWcsIC5obGpzLWZvcm11bGEgewogIGNvbG9yOiAjYzY3OGRkOwp9Ci5obGpzLXNlY3Rpb24sIC5obGpzLW5hbWUsIC5obGpzLXNlbGVjdG9yLXRhZywgLmhsanMtZGVsZXRpb24sIC5obGpzLXN1YnN0IHsKICBjb2xvcjogI2UwNmM3NTsKfQouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzU2YjZjMjsKfQouaGxqcy1zdHJpbmcsIC5obGpzLXJlZ2V4cCwgLmhsanMtYWRkaXRpb24sIC5obGpzLWF0dHJpYnV0ZSwgLmhsanMtbWV0YS1zdHJpbmcgewogIGNvbG9yOiAjOThjMzc5Owp9Ci5obGpzLWJ1aWx0X2luLCAuaGxqcy1jbGFzcyAuaGxqcy10aXRsZSB7CiAgY29sb3I6ICNlNmMwN2I7Cn0KLmhsanMtYXR0ciwgLmhsanMtdmFyaWFibGUsIC5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLCAuaGxqcy10eXBlLCAuaGxqcy1zZWxlY3Rvci1jbGFzcywgLmhsanMtc2VsZWN0b3ItYXR0ciwgLmhsanMtc2VsZWN0b3ItcHNldWRvLCAuaGxqcy1udW1iZXIgewogIGNvbG9yOiAjZDE5YTY2Owp9Ci5obGpzLXN5bWJvbCwgLmhsanMtYnVsbGV0LCAuaGxqcy1saW5rLCAuaGxqcy1tZXRhLCAuaGxqcy1zZWxlY3Rvci1pZCwgLmhsanMtdGl0bGUgewogIGNvbG9yOiAjNjFhZWVlOwp9Ci5obGpzLWVtcGhhc2lzIHsKICBmb250LXN0eWxlOiBpdGFsaWM7Cn0KLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQouaGxqcy1saW5rIHsKICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTsKfQo=`;
\ No newline at end of file
diff --git a/ReactNativeClient/pluginAssets/highlight.js/atom-one-light.css.base64.js b/ReactNativeClient/pluginAssets/highlight.js/atom-one-light.css.base64.js
new file mode 100644
index 000000000..0223dfcb0
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/highlight.js/atom-one-light.css.base64.js
@@ -0,0 +1 @@
+module.exports = `LyoKCkF0b20gT25lIExpZ2h0IGJ5IERhbmllbCBHYW1hZ2UKT3JpZ2luYWwgT25lIExpZ2h0IFN5bnRheCB0aGVtZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9hdG9tL29uZS1saWdodC1zeW50YXgKCmJhc2U6ICAgICNmYWZhZmEKbW9uby0xOiAgIzM4M2E0Mgptb25vLTI6ICAjNjg2Yjc3Cm1vbm8tMzogICNhMGExYTcKaHVlLTE6ICAgIzAxODRiYgpodWUtMjogICAjNDA3OGYyCmh1ZS0zOiAgICNhNjI2YTQKaHVlLTQ6ICAgIzUwYTE0ZgpodWUtNTogICAjZTQ1NjQ5Cmh1ZS01LTI6ICNjOTEyNDMKaHVlLTY6ICAgIzk4NjgwMQpodWUtNi0yOiAjYzE4NDAxCgoqLwoKLmhsanMgewogIGRpc3BsYXk6IGJsb2NrOwogIG92ZXJmbG93LXg6IGF1dG87CiAgcGFkZGluZzogMC41ZW07CiAgY29sb3I6ICMzODNhNDI7CiAgYmFja2dyb3VuZDogI2ZhZmFmYTsKfQoKLmhsanMtY29tbWVudCwKLmhsanMtcXVvdGUgewogIGNvbG9yOiAjYTBhMWE3OwogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtZG9jdGFnLAouaGxqcy1rZXl3b3JkLAouaGxqcy1mb3JtdWxhIHsKICBjb2xvcjogI2E2MjZhNDsKfQoKLmhsanMtc2VjdGlvbiwKLmhsanMtbmFtZSwKLmhsanMtc2VsZWN0b3ItdGFnLAouaGxqcy1kZWxldGlvbiwKLmhsanMtc3Vic3QgewogIGNvbG9yOiAjZTQ1NjQ5Owp9CgouaGxqcy1saXRlcmFsIHsKICBjb2xvcjogIzAxODRiYjsKfQoKLmhsanMtc3RyaW5nLAouaGxqcy1yZWdleHAsCi5obGpzLWFkZGl0aW9uLAouaGxqcy1hdHRyaWJ1dGUsCi5obGpzLW1ldGEtc3RyaW5nIHsKICBjb2xvcjogIzUwYTE0ZjsKfQoKLmhsanMtYnVpbHRfaW4sCi5obGpzLWNsYXNzIC5obGpzLXRpdGxlIHsKICBjb2xvcjogI2MxODQwMTsKfQoKLmhsanMtYXR0ciwKLmhsanMtdmFyaWFibGUsCi5obGpzLXRlbXBsYXRlLXZhcmlhYmxlLAouaGxqcy10eXBlLAouaGxqcy1zZWxlY3Rvci1jbGFzcywKLmhsanMtc2VsZWN0b3ItYXR0ciwKLmhsanMtc2VsZWN0b3ItcHNldWRvLAouaGxqcy1udW1iZXIgewogIGNvbG9yOiAjOTg2ODAxOwp9CgouaGxqcy1zeW1ib2wsCi5obGpzLWJ1bGxldCwKLmhsanMtbGluaywKLmhsanMtbWV0YSwKLmhsanMtc2VsZWN0b3ItaWQsCi5obGpzLXRpdGxlIHsKICBjb2xvcjogIzQwNzhmMjsKfQoKLmhsanMtZW1waGFzaXMgewogIGZvbnQtc3R5bGU6IGl0YWxpYzsKfQoKLmhsanMtc3Ryb25nIHsKICBmb250LXdlaWdodDogYm9sZDsKfQoKLmhsanMtbGluayB7CiAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7Cn0K`;
\ No newline at end of file
diff --git a/ReactNativeClient/pluginAssets/index.js b/ReactNativeClient/pluginAssets/index.js
new file mode 100644
index 000000000..1c750586e
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/index.js
@@ -0,0 +1,11 @@
+module.exports = {
+ hash: '964177e31d959b03bc63f11216c61e3e', files: {
+ 'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
+ 'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
+ 'katex/fonts/KaTeX_Main-Regular.woff2': { data: require('./katex/fonts/KaTeX_Main-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
+ 'katex/fonts/KaTeX_Math-Italic.woff2': { data: require('./katex/fonts/KaTeX_Math-Italic.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
+ 'katex/fonts/KaTeX_Size1-Regular.woff2': { data: require('./katex/fonts/KaTeX_Size1-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
+ 'katex/fonts/KaTeX_Size2-Regular.woff2': { data: require('./katex/fonts/KaTeX_Size2-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
+ 'katex/katex.css': { data: require('./katex/katex.css.base64.js'), mime: 'text/css', encoding: 'base64' },
+ },
+};
diff --git a/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Main-Regular.woff2.base64.js b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Main-Regular.woff2.base64.js
new file mode 100644
index 000000000..5ef84cccc
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Main-Regular.woff2.base64.js
@@ -0,0 +1 @@
+module.exports = `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`;
\ No newline at end of file
diff --git a/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Math-Italic.woff2.base64.js b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Math-Italic.woff2.base64.js
new file mode 100644
index 000000000..cf6447e3d
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Math-Italic.woff2.base64.js
@@ -0,0 +1 @@
+module.exports = `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`;
\ No newline at end of file
diff --git a/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Size1-Regular.woff2.base64.js b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Size1-Regular.woff2.base64.js
new file mode 100644
index 000000000..ee6f98c1b
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Size1-Regular.woff2.base64.js
@@ -0,0 +1 @@
+module.exports = `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`;
\ No newline at end of file
diff --git a/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Size2-Regular.woff2.base64.js b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Size2-Regular.woff2.base64.js
new file mode 100644
index 000000000..464fc5124
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/katex/fonts/KaTeX_Size2-Regular.woff2.base64.js
@@ -0,0 +1 @@
+module.exports = `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`;
\ No newline at end of file
diff --git a/ReactNativeClient/pluginAssets/katex/katex.css.base64.js b/ReactNativeClient/pluginAssets/katex/katex.css.base64.js
new file mode 100644
index 000000000..b9a7a405f
--- /dev/null
+++ b/ReactNativeClient/pluginAssets/katex/katex.css.base64.js
@@ -0,0 +1 @@
+module.exports = `QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9BTVM7c3JjOnVybChmb250cy9LYVRlWF9BTVMtUmVndWxhci53b2ZmMikgZm9ybWF0KCJ3b2ZmMiIpLHVybChmb250cy9LYVRlWF9BTVMtUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfQU1TLVJlZ3VsYXIudHRmKSBmb3JtYXQoInRydWV0eXBlIik7Zm9udC13ZWlnaHQ6NDAwO2ZvbnQtc3R5bGU6bm9ybWFsfUBmb250LWZhY2V7Zm9udC1mYW1pbHk6S2FUZVhfQ2FsaWdyYXBoaWM7c3JjOnVybChmb250cy9LYVRlWF9DYWxpZ3JhcGhpYy1Cb2xkLndvZmYyKSBmb3JtYXQoIndvZmYyIiksdXJsKGZvbnRzL0thVGVYX0NhbGlncmFwaGljLUJvbGQud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX0NhbGlncmFwaGljLUJvbGQudHRmKSBmb3JtYXQoInRydWV0eXBlIik7Zm9udC13ZWlnaHQ6NzAwO2ZvbnQtc3R5bGU6bm9ybWFsfUBmb250LWZhY2V7Zm9udC1mYW1pbHk6S2FUZVhfQ2FsaWdyYXBoaWM7c3JjOnVybChmb250cy9LYVRlWF9DYWxpZ3JhcGhpYy1SZWd1bGFyLndvZmYyKSBmb3JtYXQoIndvZmYyIiksdXJsKGZvbnRzL0thVGVYX0NhbGlncmFwaGljLVJlZ3VsYXIud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX0NhbGlncmFwaGljLVJlZ3VsYXIudHRmKSBmb3JtYXQoInRydWV0eXBlIik7Zm9udC13ZWlnaHQ6NDAwO2ZvbnQtc3R5bGU6bm9ybWFsfUBmb250LWZhY2V7Zm9udC1mYW1pbHk6S2FUZVhfRnJha3R1cjtzcmM6dXJsKGZvbnRzL0thVGVYX0ZyYWt0dXItQm9sZC53b2ZmMikgZm9ybWF0KCJ3b2ZmMiIpLHVybChmb250cy9LYVRlWF9GcmFrdHVyLUJvbGQud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX0ZyYWt0dXItQm9sZC50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo3MDA7Zm9udC1zdHlsZTpub3JtYWx9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9GcmFrdHVyO3NyYzp1cmwoZm9udHMvS2FUZVhfRnJha3R1ci1SZWd1bGFyLndvZmYyKSBmb3JtYXQoIndvZmYyIiksdXJsKGZvbnRzL0thVGVYX0ZyYWt0dXItUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfRnJha3R1ci1SZWd1bGFyLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0eWxlOm5vcm1hbH1AZm9udC1mYWNle2ZvbnQtZmFtaWx5OkthVGVYX01haW47c3JjOnVybChmb250cy9LYVRlWF9NYWluLUJvbGQud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfTWFpbi1Cb2xkLndvZmYpIGZvcm1hdCgid29mZiIpLHVybChmb250cy9LYVRlWF9NYWluLUJvbGQudHRmKSBmb3JtYXQoInRydWV0eXBlIik7Zm9udC13ZWlnaHQ6NzAwO2ZvbnQtc3R5bGU6bm9ybWFsfUBmb250LWZhY2V7Zm9udC1mYW1pbHk6S2FUZVhfTWFpbjtzcmM6dXJsKGZvbnRzL0thVGVYX01haW4tQm9sZEl0YWxpYy53b2ZmMikgZm9ybWF0KCJ3b2ZmMiIpLHVybChmb250cy9LYVRlWF9NYWluLUJvbGRJdGFsaWMud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX01haW4tQm9sZEl0YWxpYy50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo3MDA7Zm9udC1zdHlsZTppdGFsaWN9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9NYWluO3NyYzp1cmwoZm9udHMvS2FUZVhfTWFpbi1JdGFsaWMud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfTWFpbi1JdGFsaWMud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX01haW4tSXRhbGljLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0eWxlOml0YWxpY31AZm9udC1mYWNle2ZvbnQtZmFtaWx5OkthVGVYX01haW47c3JjOnVybChmb250cy9LYVRlWF9NYWluLVJlZ3VsYXIud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfTWFpbi1SZWd1bGFyLndvZmYpIGZvcm1hdCgid29mZiIpLHVybChmb250cy9LYVRlWF9NYWluLVJlZ3VsYXIudHRmKSBmb3JtYXQoInRydWV0eXBlIik7Zm9udC13ZWlnaHQ6NDAwO2ZvbnQtc3R5bGU6bm9ybWFsfUBmb250LWZhY2V7Zm9udC1mYW1pbHk6S2FUZVhfTWF0aDtzcmM6dXJsKGZvbnRzL0thVGVYX01hdGgtQm9sZEl0YWxpYy53b2ZmMikgZm9ybWF0KCJ3b2ZmMiIpLHVybChmb250cy9LYVRlWF9NYXRoLUJvbGRJdGFsaWMud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX01hdGgtQm9sZEl0YWxpYy50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo3MDA7Zm9udC1zdHlsZTppdGFsaWN9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9NYXRoO3NyYzp1cmwoZm9udHMvS2FUZVhfTWF0aC1JdGFsaWMud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfTWF0aC1JdGFsaWMud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX01hdGgtSXRhbGljLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0eWxlOml0YWxpY31AZm9udC1mYWNle2ZvbnQtZmFtaWx5OiJLYVRlWF9TYW5zU2VyaWYiO3NyYzp1cmwoZm9udHMvS2FUZVhfU2Fuc1NlcmlmLUJvbGQud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfU2Fuc1NlcmlmLUJvbGQud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX1NhbnNTZXJpZi1Cb2xkLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjcwMDtmb250LXN0eWxlOm5vcm1hbH1AZm9udC1mYWNle2ZvbnQtZmFtaWx5OiJLYVRlWF9TYW5zU2VyaWYiO3NyYzp1cmwoZm9udHMvS2FUZVhfU2Fuc1NlcmlmLUl0YWxpYy53b2ZmMikgZm9ybWF0KCJ3b2ZmMiIpLHVybChmb250cy9LYVRlWF9TYW5zU2VyaWYtSXRhbGljLndvZmYpIGZvcm1hdCgid29mZiIpLHVybChmb250cy9LYVRlWF9TYW5zU2VyaWYtSXRhbGljLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0eWxlOml0YWxpY31AZm9udC1mYWNle2ZvbnQtZmFtaWx5OiJLYVRlWF9TYW5zU2VyaWYiO3NyYzp1cmwoZm9udHMvS2FUZVhfU2Fuc1NlcmlmLVJlZ3VsYXIud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfU2Fuc1NlcmlmLVJlZ3VsYXIud29mZikgZm9ybWF0KCJ3b2ZmIiksdXJsKGZvbnRzL0thVGVYX1NhbnNTZXJpZi1SZWd1bGFyLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0eWxlOm5vcm1hbH1AZm9udC1mYWNle2ZvbnQtZmFtaWx5OkthVGVYX1NjcmlwdDtzcmM6dXJsKGZvbnRzL0thVGVYX1NjcmlwdC1SZWd1bGFyLndvZmYyKSBmb3JtYXQoIndvZmYyIiksdXJsKGZvbnRzL0thVGVYX1NjcmlwdC1SZWd1bGFyLndvZmYpIGZvcm1hdCgid29mZiIpLHVybChmb250cy9LYVRlWF9TY3JpcHQtUmVndWxhci50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo0MDA7Zm9udC1zdHlsZTpub3JtYWx9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9TaXplMTtzcmM6dXJsKGZvbnRzL0thVGVYX1NpemUxLVJlZ3VsYXIud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTEtUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTEtUmVndWxhci50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo0MDA7Zm9udC1zdHlsZTpub3JtYWx9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9TaXplMjtzcmM6dXJsKGZvbnRzL0thVGVYX1NpemUyLVJlZ3VsYXIud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTItUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTItUmVndWxhci50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo0MDA7Zm9udC1zdHlsZTpub3JtYWx9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9TaXplMztzcmM6dXJsKGZvbnRzL0thVGVYX1NpemUzLVJlZ3VsYXIud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTMtUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTMtUmVndWxhci50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo0MDA7Zm9udC1zdHlsZTpub3JtYWx9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9TaXplNDtzcmM6dXJsKGZvbnRzL0thVGVYX1NpemU0LVJlZ3VsYXIud29mZjIpIGZvcm1hdCgid29mZjIiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTQtUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfU2l6ZTQtUmVndWxhci50dGYpIGZvcm1hdCgidHJ1ZXR5cGUiKTtmb250LXdlaWdodDo0MDA7Zm9udC1zdHlsZTpub3JtYWx9QGZvbnQtZmFjZXtmb250LWZhbWlseTpLYVRlWF9UeXBld3JpdGVyO3NyYzp1cmwoZm9udHMvS2FUZVhfVHlwZXdyaXRlci1SZWd1bGFyLndvZmYyKSBmb3JtYXQoIndvZmYyIiksdXJsKGZvbnRzL0thVGVYX1R5cGV3cml0ZXItUmVndWxhci53b2ZmKSBmb3JtYXQoIndvZmYiKSx1cmwoZm9udHMvS2FUZVhfVHlwZXdyaXRlci1SZWd1bGFyLnR0ZikgZm9ybWF0KCJ0cnVldHlwZSIpO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0eWxlOm5vcm1hbH0ua2F0ZXh7Zm9udDpub3JtYWwgMS4yMWVtIEthVGVYX01haW4sVGltZXMgTmV3IFJvbWFuLHNlcmlmO2xpbmUtaGVpZ2h0OjEuMjt0ZXh0LWluZGVudDowO3RleHQtcmVuZGVyaW5nOmF1dG99LmthdGV4ICp7LW1zLWhpZ2gtY29udHJhc3QtYWRqdXN0Om5vbmUhaW1wb3J0YW50fS5rYXRleCAua2F0ZXgtdmVyc2lvbjphZnRlcntjb250ZW50OiIwLjExLjEifS5rYXRleCAua2F0ZXgtbWF0aG1se3Bvc2l0aW9uOmFic29sdXRlO2NsaXA6cmVjdCgxcHgsMXB4LDFweCwxcHgpO3BhZGRpbmc6MDtib3JkZXI6MDtoZWlnaHQ6MXB4O3dpZHRoOjFweDtvdmVyZmxvdzpoaWRkZW59LmthdGV4IC5rYXRleC1odG1sPi5uZXdsaW5le2Rpc3BsYXk6YmxvY2t9LmthdGV4IC5iYXNle3Bvc2l0aW9uOnJlbGF0aXZlO3doaXRlLXNwYWNlOm5vd3JhcDt3aWR0aDptaW4tY29udGVudH0ua2F0ZXggLmJhc2UsLmthdGV4IC5zdHJ1dHtkaXNwbGF5OmlubGluZS1ibG9ja30ua2F0ZXggLnRleHRiZntmb250LXdlaWdodDo3MDB9LmthdGV4IC50ZXh0aXR7Zm9udC1zdHlsZTppdGFsaWN9LmthdGV4IC50ZXh0cm17Zm9udC1mYW1pbHk6S2FUZVhfTWFpbn0ua2F0ZXggLnRleHRzZntmb250LWZhbWlseTpLYVRlWF9TYW5zU2VyaWZ9LmthdGV4IC50ZXh0dHR7Zm9udC1mYW1pbHk6S2FUZVhfVHlwZXdyaXRlcn0ua2F0ZXggLm1hdGhkZWZhdWx0e2ZvbnQtZmFtaWx5OkthVGVYX01hdGg7Zm9udC1zdHlsZTppdGFsaWN9LmthdGV4IC5tYXRoaXR7Zm9udC1mYW1pbHk6S2FUZVhfTWFpbjtmb250LXN0eWxlOml0YWxpY30ua2F0ZXggLm1hdGhybXtmb250LXN0eWxlOm5vcm1hbH0ua2F0ZXggLm1hdGhiZntmb250LWZhbWlseTpLYVRlWF9NYWluO2ZvbnQtd2VpZ2h0OjcwMH0ua2F0ZXggLmJvbGRzeW1ib2x7Zm9udC1mYW1pbHk6S2FUZVhfTWF0aDtmb250LXdlaWdodDo3MDA7Zm9udC1zdHlsZTppdGFsaWN9LmthdGV4IC5hbXNybSwua2F0ZXggLm1hdGhiYiwua2F0ZXggLnRleHRiYntmb250LWZhbWlseTpLYVRlWF9BTVN9LmthdGV4IC5tYXRoY2Fse2ZvbnQtZmFtaWx5OkthVGVYX0NhbGlncmFwaGljfS5rYXRleCAubWF0aGZyYWssLmthdGV4IC50ZXh0ZnJha3tmb250LWZhbWlseTpLYVRlWF9GcmFrdHVyfS5rYXRleCAubWF0aHR0e2ZvbnQtZmFtaWx5OkthVGVYX1R5cGV3cml0ZXJ9LmthdGV4IC5tYXRoc2NyLC5rYXRleCAudGV4dHNjcntmb250LWZhbWlseTpLYVRlWF9TY3JpcHR9LmthdGV4IC5tYXRoc2YsLmthdGV4IC50ZXh0c2Z7Zm9udC1mYW1pbHk6S2FUZVhfU2Fuc1NlcmlmfS5rYXRleCAubWF0aGJvbGRzZiwua2F0ZXggLnRleHRib2xkc2Z7Zm9udC1mYW1pbHk6S2FUZVhfU2Fuc1NlcmlmO2ZvbnQtd2VpZ2h0OjcwMH0ua2F0ZXggLm1hdGhpdHNmLC5rYXRleCAudGV4dGl0c2Z7Zm9udC1mYW1pbHk6S2FUZVhfU2Fuc1NlcmlmO2ZvbnQtc3R5bGU6aXRhbGljfS5rYXRleCAubWFpbnJte2ZvbnQtZmFtaWx5OkthVGVYX01haW47Zm9udC1zdHlsZTpub3JtYWx9LmthdGV4IC52bGlzdC10e2Rpc3BsYXk6aW5saW5lLXRhYmxlO3RhYmxlLWxheW91dDpmaXhlZH0ua2F0ZXggLnZsaXN0LXJ7ZGlzcGxheTp0YWJsZS1yb3d9LmthdGV4IC52bGlzdHtkaXNwbGF5OnRhYmxlLWNlbGw7dmVydGljYWwtYWxpZ246Ym90dG9tO3Bvc2l0aW9uOnJlbGF0aXZlfS5rYXRleCAudmxpc3Q+c3BhbntkaXNwbGF5OmJsb2NrO2hlaWdodDowO3Bvc2l0aW9uOnJlbGF0aXZlfS5rYXRleCAudmxpc3Q+c3Bhbj5zcGFue2Rpc3BsYXk6aW5saW5lLWJsb2NrfS5rYXRleCAudmxpc3Q+c3Bhbj4ucHN0cnV0e292ZXJmbG93OmhpZGRlbjt3aWR0aDowfS5rYXRleCAudmxpc3QtdDJ7bWFyZ2luLXJpZ2h0Oi0ycHh9LmthdGV4IC52bGlzdC1ze2Rpc3BsYXk6dGFibGUtY2VsbDt2ZXJ0aWNhbC1hbGlnbjpib3R0b207Zm9udC1zaXplOjFweDt3aWR0aDoycHg7bWluLXdpZHRoOjJweH0ua2F0ZXggLm1zdXBzdWJ7dGV4dC1hbGlnbjpsZWZ0fS5rYXRleCAubWZyYWM+c3Bhbj5zcGFue3RleHQtYWxpZ246Y2VudGVyfS5rYXRleCAubWZyYWMgLmZyYWMtbGluZXtkaXNwbGF5OmlubGluZS1ibG9jazt3aWR0aDoxMDAlO2JvcmRlci1ib3R0b20tc3R5bGU6c29saWR9LmthdGV4IC5oZGFzaGxpbmUsLmthdGV4IC5obGluZSwua2F0ZXggLm1mcmFjIC5mcmFjLWxpbmUsLmthdGV4IC5vdmVybGluZSAub3ZlcmxpbmUtbGluZSwua2F0ZXggLnJ1bGUsLmthdGV4IC51bmRlcmxpbmUgLnVuZGVybGluZS1saW5le21pbi1oZWlnaHQ6MXB4fS5rYXRleCAubXNwYWNle2Rpc3BsYXk6aW5saW5lLWJsb2NrfS5rYXRleCAuY2xhcCwua2F0ZXggLmxsYXAsLmthdGV4IC5ybGFwe3dpZHRoOjA7cG9zaXRpb246cmVsYXRpdmV9LmthdGV4IC5jbGFwPi5pbm5lciwua2F0ZXggLmxsYXA+LmlubmVyLC5rYXRleCAucmxhcD4uaW5uZXJ7cG9zaXRpb246YWJzb2x1dGV9LmthdGV4IC5jbGFwPi5maXgsLmthdGV4IC5sbGFwPi5maXgsLmthdGV4IC5ybGFwPi5maXh7ZGlzcGxheTppbmxpbmUtYmxvY2t9LmthdGV4IC5sbGFwPi5pbm5lcntyaWdodDowfS5rYXRleCAuY2xhcD4uaW5uZXIsLmthdGV4IC5ybGFwPi5pbm5lcntsZWZ0OjB9LmthdGV4IC5jbGFwPi5pbm5lcj5zcGFue21hcmdpbi1sZWZ0Oi01MCU7bWFyZ2luLXJpZ2h0OjUwJX0ua2F0ZXggLnJ1bGV7ZGlzcGxheTppbmxpbmUtYmxvY2s7Ym9yZGVyOjAgc29saWQ7cG9zaXRpb246cmVsYXRpdmV9LmthdGV4IC5obGluZSwua2F0ZXggLm92ZXJsaW5lIC5vdmVybGluZS1saW5lLC5rYXRleCAudW5kZXJsaW5lIC51bmRlcmxpbmUtbGluZXtkaXNwbGF5OmlubGluZS1ibG9jazt3aWR0aDoxMDAlO2JvcmRlci1ib3R0b20tc3R5bGU6c29saWR9LmthdGV4IC5oZGFzaGxpbmV7ZGlzcGxheTppbmxpbmUtYmxvY2s7d2lkdGg6MTAwJTtib3JkZXItYm90dG9tLXN0eWxlOmRhc2hlZH0ua2F0ZXggLnNxcnQ+LnJvb3R7bWFyZ2luLWxlZnQ6LjI3Nzc3Nzc4ZW07bWFyZ2luLXJpZ2h0Oi0uNTU1NTU1NTZlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEuc2l6ZTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTF7Zm9udC1zaXplOjFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEuc2l6ZTIsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTJ7Zm9udC1zaXplOjEuMmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMywua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplM3tmb250LXNpemU6MS40ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxLnNpemU0LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxLnNpemU0e2ZvbnQtc2l6ZToxLjZlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEuc2l6ZTUsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTV7Zm9udC1zaXplOjEuOGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplNiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplNntmb250LXNpemU6MmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplNywua2F0ZXggLnNpemluZy5yZXNldC1zaXplMS5zaXplN3tmb250LXNpemU6Mi40ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxLnNpemU4LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxLnNpemU4e2ZvbnQtc2l6ZToyLjg4ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxLnNpemU5LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxLnNpemU5e2ZvbnQtc2l6ZTozLjQ1NmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMTAsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTEwe2ZvbnQtc2l6ZTo0LjE0OGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMS5zaXplMTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEuc2l6ZTExe2ZvbnQtc2l6ZTo0Ljk3NmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMi5zaXplMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplMXtmb250LXNpemU6LjgzMzMzMzMzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemUyLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemUye2ZvbnQtc2l6ZToxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemUze2ZvbnQtc2l6ZToxLjE2NjY2NjY3ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemU0LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemU0e2ZvbnQtc2l6ZToxLjMzMzMzMzMzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemU1LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUyLnNpemU1e2ZvbnQtc2l6ZToxLjVlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTYsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTIuc2l6ZTZ7Zm9udC1zaXplOjEuNjY2NjY2NjdlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTcsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTIuc2l6ZTd7Zm9udC1zaXplOjJlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTIuc2l6ZTgsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTIuc2l6ZTh7Zm9udC1zaXplOjIuNGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMi5zaXplOSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplOXtmb250LXNpemU6Mi44OGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMi5zaXplMTAsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTIuc2l6ZTEwe2ZvbnQtc2l6ZTozLjQ1NjY2NjY3ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUyLnNpemUxMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMi5zaXplMTF7Zm9udC1zaXplOjQuMTQ2NjY2NjdlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTMuc2l6ZTF7Zm9udC1zaXplOi43MTQyODU3MWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMy5zaXplMiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMy5zaXplMntmb250LXNpemU6Ljg1NzE0Mjg2ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemUze2ZvbnQtc2l6ZToxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemU0LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU0e2ZvbnQtc2l6ZToxLjE0Mjg1NzE0ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemU1LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU1e2ZvbnQtc2l6ZToxLjI4NTcxNDI5ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemU2LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU2e2ZvbnQtc2l6ZToxLjQyODU3MTQzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemU3LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU3e2ZvbnQtc2l6ZToxLjcxNDI4NTcxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemU4LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU4e2ZvbnQtc2l6ZToyLjA1NzE0Mjg2ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemU5LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemU5e2ZvbnQtc2l6ZToyLjQ2ODU3MTQzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUzLnNpemUxMCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMy5zaXplMTB7Zm9udC1zaXplOjIuOTYyODU3MTRlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTMuc2l6ZTExLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUzLnNpemUxMXtmb250LXNpemU6My41NTQyODU3MWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplMXtmb250LXNpemU6LjYyNWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplMiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplMntmb250LXNpemU6Ljc1ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemUze2ZvbnQtc2l6ZTouODc1ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemU0LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemU0e2ZvbnQtc2l6ZToxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemU1LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemU1e2ZvbnQtc2l6ZToxLjEyNWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplNiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplNntmb250LXNpemU6MS4yNWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNC5zaXplNywua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplN3tmb250LXNpemU6MS41ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemU4LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemU4e2ZvbnQtc2l6ZToxLjhlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTQuc2l6ZTksLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTQuc2l6ZTl7Zm9udC1zaXplOjIuMTZlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTQuc2l6ZTEwLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU0LnNpemUxMHtmb250LXNpemU6Mi41OTI1ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU0LnNpemUxMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNC5zaXplMTF7Zm9udC1zaXplOjMuMTFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTUuc2l6ZTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTF7Zm9udC1zaXplOi41NTU1NTU1NmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplMiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplMntmb250LXNpemU6LjY2NjY2NjY3ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU1LnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU1LnNpemUze2ZvbnQtc2l6ZTouNzc3Nzc3NzhlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTUuc2l6ZTQsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTUuc2l6ZTR7Zm9udC1zaXplOi44ODg4ODg4OWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplNXtmb250LXNpemU6MWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplNntmb250LXNpemU6MS4xMTExMTExMWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplNywua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplN3tmb250LXNpemU6MS4zMzMzMzMzM2VtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNS5zaXplOCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplOHtmb250LXNpemU6MS42ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU1LnNpemU5LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU1LnNpemU5e2ZvbnQtc2l6ZToxLjkyZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU1LnNpemUxMCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNS5zaXplMTB7Zm9udC1zaXplOjIuMzA0NDQ0NDRlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTUuc2l6ZTExLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU1LnNpemUxMXtmb250LXNpemU6Mi43NjQ0NDQ0NGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNi5zaXplMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNi5zaXplMXtmb250LXNpemU6LjVlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTIsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTJ7Zm9udC1zaXplOi42ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemUze2ZvbnQtc2l6ZTouN2VtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNi5zaXplNCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNi5zaXplNHtmb250LXNpemU6LjhlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTUsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTV7Zm9udC1zaXplOi45ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemU2LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemU2e2ZvbnQtc2l6ZToxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemU3LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU2LnNpemU3e2ZvbnQtc2l6ZToxLjJlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTgsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTh7Zm9udC1zaXplOjEuNDRlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTYuc2l6ZTksLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTYuc2l6ZTl7Zm9udC1zaXplOjEuNzI4ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemUxMCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNi5zaXplMTB7Zm9udC1zaXplOjIuMDc0ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU2LnNpemUxMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNi5zaXplMTF7Zm9udC1zaXplOjIuNDg4ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU3LnNpemUxLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU3LnNpemUxe2ZvbnQtc2l6ZTouNDE2NjY2NjdlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTcuc2l6ZTIsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTJ7Zm9udC1zaXplOi41ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU3LnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU3LnNpemUze2ZvbnQtc2l6ZTouNTgzMzMzMzNlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTcuc2l6ZTQsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTR7Zm9udC1zaXplOi42NjY2NjY2N2VtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplNSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNy5zaXplNXtmb250LXNpemU6Ljc1ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU3LnNpemU2LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU3LnNpemU2e2ZvbnQtc2l6ZTouODMzMzMzMzNlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTcuc2l6ZTcsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTd7Zm9udC1zaXplOjFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTcuc2l6ZTgsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTh7Zm9udC1zaXplOjEuMmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplOSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNy5zaXplOXtmb250LXNpemU6MS40NGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplNy5zaXplMTAsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTcuc2l6ZTEwe2ZvbnQtc2l6ZToxLjcyODMzMzMzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU3LnNpemUxMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplNy5zaXplMTF7Zm9udC1zaXplOjIuMDczMzMzMzNlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTF7Zm9udC1zaXplOi4zNDcyMjIyMmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplMiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplOC5zaXplMntmb250LXNpemU6LjQxNjY2NjY3ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU4LnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU4LnNpemUze2ZvbnQtc2l6ZTouNDg2MTExMTFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTQsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTR7Zm9udC1zaXplOi41NTU1NTU1NmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplNSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplOC5zaXplNXtmb250LXNpemU6LjYyNWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplNiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplOC5zaXplNntmb250LXNpemU6LjY5NDQ0NDQ0ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU4LnNpemU3LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU4LnNpemU3e2ZvbnQtc2l6ZTouODMzMzMzMzNlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTgsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTh7Zm9udC1zaXplOjFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTguc2l6ZTksLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTl7Zm9udC1zaXplOjEuMmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOC5zaXplMTAsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTguc2l6ZTEwe2ZvbnQtc2l6ZToxLjQ0MDI3Nzc4ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU4LnNpemUxMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplOC5zaXplMTF7Zm9udC1zaXplOjEuNzI3Nzc3NzhlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTF7Zm9udC1zaXplOi4yODkzNTE4NWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplMiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplOS5zaXplMntmb250LXNpemU6LjM0NzIyMjIyZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU5LnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemUze2ZvbnQtc2l6ZTouNDA1MDkyNTllbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTQsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTR7Zm9udC1zaXplOi40NjI5NjI5NmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplNSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplOS5zaXplNXtmb250LXNpemU6LjUyMDgzMzMzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU5LnNpemU2LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemU2e2ZvbnQtc2l6ZTouNTc4NzAzN2VtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplNywua2F0ZXggLnNpemluZy5yZXNldC1zaXplOS5zaXplN3tmb250LXNpemU6LjY5NDQ0NDQ0ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemU5LnNpemU4LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemU4e2ZvbnQtc2l6ZTouODMzMzMzMzNlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTksLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTl7Zm9udC1zaXplOjFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTkuc2l6ZTEwLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemU5LnNpemUxMHtmb250LXNpemU6MS4yMDAyMzE0OGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplOS5zaXplMTEsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTkuc2l6ZTExe2ZvbnQtc2l6ZToxLjQzOTgxNDgxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTF7Zm9udC1zaXplOi4yNDEwODAwNGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTIsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemUye2ZvbnQtc2l6ZTouMjg5Mjk2MDVlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemUzLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplM3tmb250LXNpemU6LjMzNzUxMjA1ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplNCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTR7Zm9udC1zaXplOi4zODU3MjgwNmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTUsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemU1e2ZvbnQtc2l6ZTouNDMzOTQ0MDdlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemU2LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplNntmb250LXNpemU6LjQ4MjE2MDA4ZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplNywua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTAuc2l6ZTd7Zm9udC1zaXplOi41Nzg1OTIwOWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTgsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemU4e2ZvbnQtc2l6ZTouNjk0MzEwNTFlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTEwLnNpemU5LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplOXtmb250LXNpemU6LjgzMzE3MjYxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMC5zaXplMTAsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTEwLnNpemUxMHtmb250LXNpemU6MWVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTAuc2l6ZTExLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMC5zaXplMTF7Zm9udC1zaXplOjEuMTk5NjE0MjdlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemUxLC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplMXtmb250LXNpemU6LjIwMDk2NDYzZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplMiwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTJ7Zm9udC1zaXplOi4yNDExNTc1NmVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTMsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemUze2ZvbnQtc2l6ZTouMjgxMzUwNDhlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemU0LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplNHtmb250LXNpemU6LjMyMTU0MzQxZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplNSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTV7Zm9udC1zaXplOi4zNjE3MzYzM2VtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTYsLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemU2e2ZvbnQtc2l6ZTouNDAxOTI5MjZlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemU3LC5rYXRleCAuc2l6aW5nLnJlc2V0LXNpemUxMS5zaXplN3tmb250LXNpemU6LjQ4MjMxNTExZW19LmthdGV4IC5mb250c2l6ZS1lbnN1cmVyLnJlc2V0LXNpemUxMS5zaXplOCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTh7Zm9udC1zaXplOi41Nzg3NzgxNGVtfS5rYXRleCAuZm9udHNpemUtZW5zdXJlci5yZXNldC1zaXplMTEuc2l6ZTksLmthdGV4IC5zaXppbmcucmVzZXQtc2l6ZTExLnNpemU5e2ZvbnQtc2l6ZTouNjk0NTMzNzZlbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemUxMCwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTEwe2ZvbnQtc2l6ZTouODMzNjAxMjllbX0ua2F0ZXggLmZvbnRzaXplLWVuc3VyZXIucmVzZXQtc2l6ZTExLnNpemUxMSwua2F0ZXggLnNpemluZy5yZXNldC1zaXplMTEuc2l6ZTExe2ZvbnQtc2l6ZToxZW19LmthdGV4IC5kZWxpbXNpemluZy5zaXplMXtmb250LWZhbWlseTpLYVRlWF9TaXplMX0ua2F0ZXggLmRlbGltc2l6aW5nLnNpemUye2ZvbnQtZmFtaWx5OkthVGVYX1NpemUyfS5rYXRleCAuZGVsaW1zaXppbmcuc2l6ZTN7Zm9udC1mYW1pbHk6S2FUZVhfU2l6ZTN9LmthdGV4IC5kZWxpbXNpemluZy5zaXplNHtmb250LWZhbWlseTpLYVRlWF9TaXplNH0ua2F0ZXggLmRlbGltc2l6aW5nLm11bHQgLmRlbGltLXNpemUxPnNwYW57Zm9udC1mYW1pbHk6S2FUZVhfU2l6ZTF9LmthdGV4IC5kZWxpbXNpemluZy5tdWx0IC5kZWxpbS1zaXplND5zcGFue2ZvbnQtZmFtaWx5OkthVGVYX1NpemU0fS5rYXRleCAubnVsbGRlbGltaXRlcntkaXNwbGF5OmlubGluZS1ibG9jazt3aWR0aDouMTJlbX0ua2F0ZXggLmRlbGltY2VudGVyLC5rYXRleCAub3Atc3ltYm9se3Bvc2l0aW9uOnJlbGF0aXZlfS5rYXRleCAub3Atc3ltYm9sLnNtYWxsLW9we2ZvbnQtZmFtaWx5OkthVGVYX1NpemUxfS5rYXRleCAub3Atc3ltYm9sLmxhcmdlLW9we2ZvbnQtZmFtaWx5OkthVGVYX1NpemUyfS5rYXRleCAub3AtbGltaXRzPi52bGlzdC10e3RleHQtYWxpZ246Y2VudGVyfS5rYXRleCAuYWNjZW50Pi52bGlzdC10e3RleHQtYWxpZ246Y2VudGVyfS5rYXRleCAuYWNjZW50IC5hY2NlbnQtYm9keXtwb3NpdGlvbjpyZWxhdGl2ZX0ua2F0ZXggLmFjY2VudCAuYWNjZW50LWJvZHk6bm90KC5hY2NlbnQtZnVsbCl7d2lkdGg6MH0ua2F0ZXggLm92ZXJsYXl7ZGlzcGxheTpibG9ja30ua2F0ZXggLm10YWJsZSAudmVydGljYWwtc2VwYXJhdG9ye2Rpc3BsYXk6aW5saW5lLWJsb2NrO21pbi13aWR0aDoxcHh9LmthdGV4IC5tdGFibGUgLmFycmF5Y29sc2Vwe2Rpc3BsYXk6aW5saW5lLWJsb2NrfS5rYXRleCAubXRhYmxlIC5jb2wtYWxpZ24tYz4udmxpc3QtdHt0ZXh0LWFsaWduOmNlbnRlcn0ua2F0ZXggLm10YWJsZSAuY29sLWFsaWduLWw+LnZsaXN0LXR7dGV4dC1hbGlnbjpsZWZ0fS5rYXRleCAubXRhYmxlIC5jb2wtYWxpZ24tcj4udmxpc3QtdHt0ZXh0LWFsaWduOnJpZ2h0fS5rYXRleCAuc3ZnLWFsaWdue3RleHQtYWxpZ246bGVmdH0ua2F0ZXggc3Zne2Rpc3BsYXk6YmxvY2s7cG9zaXRpb246YWJzb2x1dGU7d2lkdGg6MTAwJTtoZWlnaHQ6aW5oZXJpdDtmaWxsOmN1cnJlbnRDb2xvcjtzdHJva2U6Y3VycmVudENvbG9yO2ZpbGwtcnVsZTpub256ZXJvO2ZpbGwtb3BhY2l0eToxO3N0cm9rZS13aWR0aDoxO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MX0ua2F0ZXggc3ZnIHBhdGh7c3Ryb2tlOm5vbmV9LmthdGV4IGltZ3tib3JkZXItc3R5bGU6bm9uZTttaW4td2lkdGg6MDttaW4taGVpZ2h0OjA7bWF4LXdpZHRoOm5vbmU7bWF4LWhlaWdodDpub25lfS5rYXRleCAuc3RyZXRjaHl7d2lkdGg6MTAwJTtkaXNwbGF5OmJsb2NrO3Bvc2l0aW9uOnJlbGF0aXZlO292ZXJmbG93OmhpZGRlbn0ua2F0ZXggLnN0cmV0Y2h5OmFmdGVyLC5rYXRleCAuc3RyZXRjaHk6YmVmb3Jle2NvbnRlbnQ6IiJ9LmthdGV4IC5oaWRlLXRhaWx7d2lkdGg6MTAwJTtwb3NpdGlvbjpyZWxhdGl2ZTtvdmVyZmxvdzpoaWRkZW59LmthdGV4IC5oYWxmYXJyb3ctbGVmdHtwb3NpdGlvbjphYnNvbHV0ZTtsZWZ0OjA7d2lkdGg6NTAuMiU7b3ZlcmZsb3c6aGlkZGVufS5rYXRleCAuaGFsZmFycm93LXJpZ2h0e3Bvc2l0aW9uOmFic29sdXRlO3JpZ2h0OjA7d2lkdGg6NTAuMiU7b3ZlcmZsb3c6aGlkZGVufS5rYXRleCAuYnJhY2UtbGVmdHtwb3NpdGlvbjphYnNvbHV0ZTtsZWZ0OjA7d2lkdGg6MjUuMSU7b3ZlcmZsb3c6aGlkZGVufS5rYXRleCAuYnJhY2UtY2VudGVye3Bvc2l0aW9uOmFic29sdXRlO2xlZnQ6MjUlO3dpZHRoOjUwJTtvdmVyZmxvdzpoaWRkZW59LmthdGV4IC5icmFjZS1yaWdodHtwb3NpdGlvbjphYnNvbHV0ZTtyaWdodDowO3dpZHRoOjI1LjElO292ZXJmbG93OmhpZGRlbn0ua2F0ZXggLngtYXJyb3ctcGFke3BhZGRpbmc6MCAuNWVtfS5rYXRleCAubW92ZXIsLmthdGV4IC5tdW5kZXIsLmthdGV4IC54LWFycm93e3RleHQtYWxpZ246Y2VudGVyfS5rYXRleCAuYm94cGFke3BhZGRpbmc6MCAuM2VtfS5rYXRleCAuZmJveCwua2F0ZXggLmZjb2xvcmJveHtib3gtc2l6aW5nOmJvcmRlci1ib3g7Ym9yZGVyOi4wNGVtIHNvbGlkfS5rYXRleCAuY2FuY2VsLXBhZHtwYWRkaW5nOjAgLjJlbX0ua2F0ZXggLmNhbmNlbC1sYXB7bWFyZ2luLWxlZnQ6LS4yZW07bWFyZ2luLXJpZ2h0Oi0uMmVtfS5rYXRleCAuc291dHtib3JkZXItYm90dG9tLXN0eWxlOnNvbGlkO2JvcmRlci1ib3R0b20td2lkdGg6LjA4ZW19LmthdGV4LWRpc3BsYXl7ZGlzcGxheTpibG9jazttYXJnaW46MWVtIDA7dGV4dC1hbGlnbjpjZW50ZXJ9LmthdGV4LWRpc3BsYXk+LmthdGV4e2Rpc3BsYXk6YmxvY2s7dGV4dC1hbGlnbjpjZW50ZXI7d2hpdGUtc3BhY2U6bm93cmFwfS5rYXRleC1kaXNwbGF5Pi5rYXRleD4ua2F0ZXgtaHRtbHtkaXNwbGF5OmJsb2NrO3Bvc2l0aW9uOnJlbGF0aXZlfS5rYXRleC1kaXNwbGF5Pi5rYXRleD4ua2F0ZXgtaHRtbD4udGFne3Bvc2l0aW9uOmFic29sdXRlO3JpZ2h0OjB9LmthdGV4LWRpc3BsYXkubGVxbm8+LmthdGV4Pi5rYXRleC1odG1sPi50YWd7bGVmdDowO3JpZ2h0OmF1dG99LmthdGV4LWRpc3BsYXkuZmxlcW4+LmthdGV4e3RleHQtYWxpZ246bGVmdH0K`;
\ No newline at end of file
diff --git a/ReactNativeClient/root.js b/ReactNativeClient/root.js
index 3a9d4001b..fbb13c932 100644
--- a/ReactNativeClient/root.js
+++ b/ReactNativeClient/root.js
@@ -80,6 +80,8 @@ const DecryptionWorker = require('lib/services/DecryptionWorker');
const EncryptionService = require('lib/services/EncryptionService');
const MigrationService = require('lib/services/MigrationService');
+import PluginAssetsLoader from './PluginAssetsLoader';
+
let storeDispatch = function() {};
const logReducerAction = function(action) {
@@ -461,6 +463,9 @@ async function initialize(dispatch) {
Setting.setValue('welcome.enabled', false);
}
+ PluginAssetsLoader.instance().setLogger(mainLogger);
+ await PluginAssetsLoader.instance().importAssets();
+
// eslint-disable-next-line require-atomic-updates
BaseItem.revisionService_ = RevisionService.instance();
diff --git a/Tools/copycss.js b/Tools/copycss.js
deleted file mode 100644
index b4f4676fc..000000000
--- a/Tools/copycss.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const fs = require('fs-extra');
-
-const cwd = process.cwd();
-const outputDir = `${cwd}/lib/csstojs`;
-
-async function createJsFromCss(name, filePath) {
- let css = await fs.readFile(filePath, 'utf-8');
- // eslint-disable-next-line no-useless-escape
- css = css.replace(/\`/g, '\\`');
- const js = `module.exports = \`${css}\`;`;
-
- const outputPath = `${outputDir}/${name}.css.js`;
- await fs.writeFile(outputPath, js);
-}
-
-async function main(argv) {
- await fs.mkdirp(outputDir);
- await createJsFromCss('katex', `${cwd}/node_modules/katex/dist/katex.min.css`);
- await createJsFromCss('hljs-atom-one-light', `${cwd}/node_modules/highlight.js/styles/atom-one-light.css`);
- await createJsFromCss('hljs-atom-one-dark-reasonable', `${cwd}/node_modules/highlight.js/styles/atom-one-dark-reasonable.css`);
-
- if (argv.indexOf('--copy-fonts') >= 0) {
- await fs.copy(`${cwd}/node_modules/katex/dist/fonts`, `${cwd}/gui/note-viewer/fonts`);
- }
-}
-
-main(process.argv).catch((error) => {
- console.error(error);
- process.exit(1);
-});
diff --git a/joplin.code-workspace b/joplin.code-workspace
index 133feaac1..8d3035529 100644
--- a/joplin.code-workspace
+++ b/joplin.code-workspace
@@ -2,11 +2,15 @@
"folders": [
{
"name": "Joplin",
- "path": ".",
- }, {
- "name": "Joplin Nextcloud App",
- "path": "D:/Web/www/nextcloud/apps/joplin",
+ "path": "."
},
+ {
+ "name": "Joplin Nextcloud App",
+ "path": "D:/Web/www/nextcloud/apps/joplin"
+ },
+ {
+ "path": "D:/Docs/PROGS/Node/joplin-renderer"
+ }
],
"settings": {
"files.exclude": {