mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-11 18:24:43 +02:00
Finished converting plugins
This commit is contained in:
parent
fe90d92e01
commit
98bf3bde8d
@ -224,7 +224,14 @@ ReactNativeClient/lib/InMemoryCache.js
|
||||
ReactNativeClient/lib/joplin-renderer/MarkupToHtml.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/code_inline.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fountain.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/highlight_keywords.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/html_image.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/image.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/katex.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/link_open.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/joplin-renderer/noteStyle.js
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -218,7 +218,14 @@ ReactNativeClient/lib/InMemoryCache.js
|
||||
ReactNativeClient/lib/joplin-renderer/MarkupToHtml.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/code_inline.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fountain.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/highlight_keywords.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/html_image.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/image.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/katex.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/link_open.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/joplin-renderer/noteStyle.js
|
||||
|
7
.ignore
7
.ignore
@ -167,7 +167,14 @@ ReactNativeClient/lib/InMemoryCache.js
|
||||
ReactNativeClient/lib/joplin-renderer/MarkupToHtml.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/code_inline.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fountain.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/highlight_keywords.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/html_image.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/image.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/katex.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/link_open.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/joplin-renderer/noteStyle.js
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { PluginStates } from 'lib/services/plugins/reducer';
|
||||
import contentScriptsToRendererRules from 'lib/services/plugins/utils/contentScriptsToRendererRules';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { ResourceInfos } from './types';
|
||||
import markupLanguageUtils from 'lib/markupLanguageUtils';
|
||||
import Setting from 'lib/models/Setting';
|
||||
@ -22,6 +22,13 @@ interface MarkupToHtmlOptions {
|
||||
export default function useMarkupToHtml(deps:HookDependencies) {
|
||||
const { themeId, customCss, plugins } = deps;
|
||||
|
||||
const markupToHtml = useMemo(() => {
|
||||
return markupLanguageUtils.newMarkupToHtml({
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
extraRendererRules: contentScriptsToRendererRules(plugins),
|
||||
});
|
||||
}, [plugins]);
|
||||
|
||||
return useCallback(async (markupLanguage: number, md: string, options: MarkupToHtmlOptions = null): Promise<any> => {
|
||||
options = {
|
||||
replaceResourceInternalToExternalLinks: false,
|
||||
@ -42,11 +49,6 @@ export default function useMarkupToHtml(deps:HookDependencies) {
|
||||
|
||||
delete options.replaceResourceInternalToExternalLinks;
|
||||
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml({
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
extraRendererRules: contentScriptsToRendererRules(plugins),
|
||||
});
|
||||
|
||||
const result = await markupToHtml.render(markupLanguage, md, theme, Object.assign({}, {
|
||||
codeTheme: theme.codeThemeCss,
|
||||
userCss: customCss || '',
|
||||
@ -57,5 +59,5 @@ export default function useMarkupToHtml(deps:HookDependencies) {
|
||||
}, options));
|
||||
|
||||
return result;
|
||||
}, [themeId, customCss, plugins]);
|
||||
}, [themeId, customCss, markupToHtml]);
|
||||
}
|
||||
|
@ -42,10 +42,6 @@ export default class MarkupToHtml {
|
||||
return this.renderers_[markupLanguage];
|
||||
}
|
||||
|
||||
injectedJavaScript() {
|
||||
return '';
|
||||
}
|
||||
|
||||
stripMarkup(markupLanguage:MarkupLanguage, markup:string, options:any = null) {
|
||||
if (!markup) return '';
|
||||
|
||||
|
@ -8,7 +8,6 @@ const md5 = require('md5');
|
||||
interface RendererRule {
|
||||
install(context:any, ruleOptions:any):any,
|
||||
assets?(theme:any):any,
|
||||
rule?: any, // TODO: remove
|
||||
plugin?: any,
|
||||
}
|
||||
|
||||
@ -29,18 +28,17 @@ interface RendererPlugins {
|
||||
const rules:RendererRules = {
|
||||
fence: require('./MdToHtml/rules/fence').default,
|
||||
sanitize_html: require('./MdToHtml/rules/sanitize_html').default,
|
||||
image: require('./MdToHtml/rules/image'),
|
||||
image: require('./MdToHtml/rules/image').default,
|
||||
checkbox: require('./MdToHtml/rules/checkbox').default,
|
||||
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'),
|
||||
katex: require('./MdToHtml/rules/katex').default,
|
||||
link_open: require('./MdToHtml/rules/link_open').default,
|
||||
html_image: require('./MdToHtml/rules/html_image').default,
|
||||
highlight_keywords: require('./MdToHtml/rules/highlight_keywords').default,
|
||||
code_inline: require('./MdToHtml/rules/code_inline').default,
|
||||
fountain: require('./MdToHtml/rules/fountain').default,
|
||||
mermaid: require('./MdToHtml/rules/mermaid').default,
|
||||
};
|
||||
|
||||
// const eventManager = require('lib/eventManager').default;
|
||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
||||
const hljs = require('highlight.js');
|
||||
const uslug = require('uslug');
|
||||
@ -101,6 +99,7 @@ interface PluginContext {
|
||||
css: any
|
||||
pluginAssets: any,
|
||||
cache: any,
|
||||
userData: any,
|
||||
}
|
||||
|
||||
interface RenderResultPluginAsset {
|
||||
@ -119,9 +118,33 @@ export interface RuleOptions {
|
||||
context: PluginContext,
|
||||
theme: any,
|
||||
postMessageSyntax: string,
|
||||
ResourceModel: any,
|
||||
resourceBaseUrl: string,
|
||||
resources: any, // resourceId: Resource
|
||||
|
||||
// Used by checkboxes to specify how it should be rendered
|
||||
checkboxRenderingType?: number,
|
||||
|
||||
// Used by the keyword highlighting plugin (mobile only)
|
||||
highlightedKeywords?: any[],
|
||||
|
||||
// Use by resource-rendering logic to signify that it should be rendered
|
||||
// as a plain HTML string without any attached JavaScript. Used for example
|
||||
// when exporting to HTML.
|
||||
plainResourceRendering?: boolean,
|
||||
|
||||
// Use in mobile app to enable long-pressing an image or a linkg
|
||||
// to display a context menu. Used in `image.ts` and `link_open.ts`
|
||||
enableLongPress?: boolean,
|
||||
|
||||
// Used in mobile app when enableLongPress = true. Tells for how long
|
||||
// the resource should be pressed before the menu is shown.
|
||||
longPressDelay?: number,
|
||||
|
||||
// Use by `link_open` rule.
|
||||
// linkRenderingType = 1 is the regular rendering and clicking on it is handled via embedded JS (in onclick attribute)
|
||||
// linkRenderingType = 2 gives a plain link with no JS. Caller needs to handle clicking on the link.
|
||||
linkRenderingType?: number,
|
||||
}
|
||||
|
||||
export default class MdToHtml {
|
||||
@ -129,7 +152,6 @@ export default class MdToHtml {
|
||||
private resourceBaseUrl_:string;
|
||||
private ResourceModel_:any;
|
||||
private contextCache_:any;
|
||||
private tempDir_:string;
|
||||
private fsDriver_:any;
|
||||
|
||||
private cachedOutputs_:any = {};
|
||||
@ -139,8 +161,9 @@ export default class MdToHtml {
|
||||
// Markdown-It plugin options (not Joplin plugin options)
|
||||
private pluginOptions_:any = {};
|
||||
private extraRendererRules_:RendererRules = {};
|
||||
private allProcessedAssets_:any = {};
|
||||
|
||||
constructor(options:Options = null) {
|
||||
public constructor(options:Options = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
// Must include last "/"
|
||||
@ -150,7 +173,6 @@ export default class MdToHtml {
|
||||
this.pluginOptions_ = options.pluginOptions ? options.pluginOptions : {};
|
||||
this.contextCache_ = inMemoryCache;
|
||||
|
||||
this.tempDir_ = options.tempDir;
|
||||
this.fsDriver_ = {
|
||||
writeFile: (/* path, content, encoding = 'base64'*/) => { throw new Error('writeFile not set'); },
|
||||
exists: (/* path*/) => { throw new Error('exists not set'); },
|
||||
@ -170,22 +192,18 @@ export default class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
fsDriver() {
|
||||
private fsDriver() {
|
||||
return this.fsDriver_;
|
||||
}
|
||||
|
||||
tempDir() {
|
||||
return this.tempDir_;
|
||||
}
|
||||
|
||||
static pluginNames() {
|
||||
public static pluginNames() {
|
||||
const output = [];
|
||||
for (const n in rules) output.push(n);
|
||||
for (const n in plugins) output.push(n);
|
||||
return output;
|
||||
}
|
||||
|
||||
pluginOptions(name:string) {
|
||||
private pluginOptions(name:string) {
|
||||
let o = this.pluginOptions_[name] ? this.pluginOptions_[name] : {};
|
||||
o = Object.assign({
|
||||
enabled: true,
|
||||
@ -193,7 +211,7 @@ export default class MdToHtml {
|
||||
return o;
|
||||
}
|
||||
|
||||
pluginEnabled(name:string) {
|
||||
private pluginEnabled(name:string) {
|
||||
return this.pluginOptions(name).enabled;
|
||||
}
|
||||
|
||||
@ -203,7 +221,7 @@ export default class MdToHtml {
|
||||
this.extraRendererRules_[id] = module;
|
||||
}
|
||||
|
||||
processPluginAssets(pluginAssets:PluginAssets):RenderResult {
|
||||
private processPluginAssets(pluginAssets:PluginAssets):RenderResult {
|
||||
const files:RenderResultPluginAsset[] = [];
|
||||
const cssStrings = [];
|
||||
for (const pluginName in pluginAssets) {
|
||||
@ -246,7 +264,13 @@ export default class MdToHtml {
|
||||
};
|
||||
}
|
||||
|
||||
private allUnprocessedAssets(theme:any) {
|
||||
// This return all the assets for all the plugins. Since it is called
|
||||
// on each render, the result is cached.
|
||||
private allProcessedAssets(theme:any, codeTheme:string) {
|
||||
const cacheKey:string = theme.cacheKey + codeTheme;
|
||||
|
||||
if (this.allProcessedAssets_[cacheKey]) return this.allProcessedAssets_[cacheKey];
|
||||
|
||||
const assets:any = {};
|
||||
for (const key in rules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
@ -257,11 +281,19 @@ export default class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
return assets;
|
||||
assets['highlight.js'] = [{ name: codeTheme }];
|
||||
|
||||
const output = this.processPluginAssets(assets);
|
||||
|
||||
this.allProcessedAssets_ = {
|
||||
[cacheKey]: output,
|
||||
};
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
async allAssets(theme:any) {
|
||||
// This is similar to allProcessedAssets() but used only by the Rich Text editor
|
||||
public async allAssets(theme:any) {
|
||||
const assets:any = {};
|
||||
for (const key in rules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
@ -278,7 +310,7 @@ export default class MdToHtml {
|
||||
return output.pluginAssets;
|
||||
}
|
||||
|
||||
async outputAssetsToExternalAssets_(output:any) {
|
||||
private async outputAssetsToExternalAssets_(output:any) {
|
||||
for (const cssString of output.cssStrings) {
|
||||
output.pluginAssets.push(await this.fsDriver().cacheCssToFile(cssString));
|
||||
}
|
||||
@ -286,7 +318,7 @@ export default class MdToHtml {
|
||||
return output;
|
||||
}
|
||||
|
||||
removeMarkdownItWrappingParagraph_(html:string) {
|
||||
private removeMarkdownItWrappingParagraph_(html:string) {
|
||||
// <p></p>\n
|
||||
if (html.length < 8) return html;
|
||||
if (html.substr(0, 3) !== '<p>') return html;
|
||||
@ -294,7 +326,7 @@ export default class MdToHtml {
|
||||
return html.substring(3, html.length - 5);
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
public clearCache() {
|
||||
this.cachedOutputs_ = {};
|
||||
}
|
||||
|
||||
@ -338,7 +370,7 @@ export default class MdToHtml {
|
||||
css: {},
|
||||
pluginAssets: {},
|
||||
cache: this.contextCache_,
|
||||
// options: ruleOptions,
|
||||
userData: {},
|
||||
};
|
||||
|
||||
const markdownIt = new MarkdownIt({
|
||||
@ -370,10 +402,6 @@ export default class MdToHtml {
|
||||
this.cachedHighlightedCode_[cacheKey] = hlCode;
|
||||
}
|
||||
|
||||
context.pluginAssets['highlight.js'] = [
|
||||
{ name: options.codeTheme },
|
||||
];
|
||||
|
||||
outputCodeHtml = hlCode;
|
||||
} catch (error) {
|
||||
outputCodeHtml = markdownIt.utils.escapeHtml(trimmedStr);
|
||||
@ -417,19 +445,14 @@ export default class MdToHtml {
|
||||
|
||||
for (const key in allRules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
const rule = allRules[key];
|
||||
if (rule.plugin) {
|
||||
const pluginOptions = {
|
||||
context: context,
|
||||
...ruleOptions,
|
||||
...(ruleOptions.plugins[key] ? ruleOptions.plugins[key] : {}),
|
||||
};
|
||||
|
||||
markdownIt.use(rule.plugin, pluginOptions);
|
||||
} else {
|
||||
const ruleInstall:Function = rule.install ? rule.install : (rule as any);
|
||||
markdownIt.use(ruleInstall(context, { ...ruleOptions }));
|
||||
}
|
||||
const rule = allRules[key];
|
||||
|
||||
markdownIt.use(rule.plugin, {
|
||||
context: context,
|
||||
...ruleOptions,
|
||||
...(ruleOptions.plugins[key] ? ruleOptions.plugins[key] : {}),
|
||||
});
|
||||
}
|
||||
|
||||
markdownIt.use(markdownItAnchor, { slugify: slugify });
|
||||
@ -440,20 +463,13 @@ export default class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
// const extraPlugins = eventManager.filterEmit('mdToHtmlPlugins', {});
|
||||
// for (const key in extraPlugins) {
|
||||
// markdownIt.use(extraPlugins[key].module, extraPlugins[key].options);
|
||||
// }
|
||||
|
||||
setupLinkify(markdownIt);
|
||||
|
||||
const renderedBody = markdownIt.render(body, context);
|
||||
|
||||
const pluginAssets = this.allUnprocessedAssets(theme);
|
||||
|
||||
let cssStrings = noteStyle(options.theme);
|
||||
|
||||
let output = this.processPluginAssets(pluginAssets); // context.pluginAssets);
|
||||
let output = { ...this.allProcessedAssets(theme, options.codeTheme) };
|
||||
cssStrings = cssStrings.concat(output.cssStrings);
|
||||
|
||||
if (options.userCss) cssStrings.push(options.userCss);
|
||||
@ -485,7 +501,4 @@ export default class MdToHtml {
|
||||
return output;
|
||||
}
|
||||
|
||||
injectedJavaScript() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
function installRule(markdownIt) {
|
||||
function plugin(markdownIt:any) {
|
||||
const defaultRender =
|
||||
markdownIt.renderer.rules.code_inline ||
|
||||
function(tokens, idx, options, env, self) {
|
||||
function(tokens:any, idx:any, options:any, _env:any, self:any) {
|
||||
return self.renderToken(tokens, idx, options);
|
||||
};
|
||||
|
||||
markdownIt.renderer.rules.code_inline = (tokens, idx, options, env, self) => {
|
||||
markdownIt.renderer.rules.code_inline = (tokens:any[], idx:number, options:any, env:any, self:any) => {
|
||||
const token = tokens[idx];
|
||||
let tokenClass = token.attrGet('class');
|
||||
if (!tokenClass) tokenClass = '';
|
||||
@ -15,8 +15,6 @@ function installRule(markdownIt) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions);
|
||||
};
|
||||
export default {
|
||||
plugin,
|
||||
};
|
@ -8,7 +8,7 @@
|
||||
// So we modify the code below to allow highlight() to return an object that tells how to render
|
||||
// the code.
|
||||
|
||||
function installRule(markdownIt:any) {
|
||||
function plugin(markdownIt:any) {
|
||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||
markdownIt.renderer.rules.fence = function(tokens:any[], idx:number, options:any, env:any, slf:any) {
|
||||
let token = tokens[idx],
|
||||
@ -63,8 +63,7 @@ function installRule(markdownIt:any) {
|
||||
};
|
||||
}
|
||||
|
||||
export default function() {
|
||||
return function(md:any) {
|
||||
installRule(md);
|
||||
};
|
||||
}
|
||||
export default {
|
||||
plugin,
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
const fountain = require('../../vendor/fountain.min.js');
|
||||
|
||||
const fountainCss = function() {
|
||||
const pluginAssets = function() {
|
||||
return [
|
||||
{
|
||||
inline: true,
|
||||
@ -102,7 +102,7 @@ const fountainCss = function() {
|
||||
];
|
||||
};
|
||||
|
||||
function renderFountainScript(markdownIt, content) {
|
||||
function renderFountainScript(markdownIt:any, content:string) {
|
||||
const result = fountain.parse(content);
|
||||
|
||||
return `
|
||||
@ -118,30 +118,19 @@ function renderFountainScript(markdownIt, content) {
|
||||
`;
|
||||
}
|
||||
|
||||
function addContextAssets(context) {
|
||||
if ('fountain' in context.pluginAssets) return;
|
||||
|
||||
context.pluginAssets['fountain'] = fountainCss();
|
||||
}
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
||||
const defaultRender = markdownIt.renderer.rules.fence || function(tokens, idx, options, env, self) {
|
||||
function plugin(markdownIt:any) {
|
||||
const defaultRender = markdownIt.renderer.rules.fence || function(tokens:any[], idx:number, options:any, _env:any, self:any) {
|
||||
return self.renderToken(tokens, idx, options);
|
||||
};
|
||||
|
||||
markdownIt.renderer.rules.fence = function(tokens, idx, options, env, self) {
|
||||
markdownIt.renderer.rules.fence = function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
||||
const token = tokens[idx];
|
||||
if (token.info !== 'fountain') return defaultRender(tokens, idx, options, env, self);
|
||||
addContextAssets(context);
|
||||
return renderFountainScript(markdownIt, token.content);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
install: function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
},
|
||||
assets: fountainCss,
|
||||
export default {
|
||||
plugin,
|
||||
assets: pluginAssets,
|
||||
};
|
@ -1,7 +1,11 @@
|
||||
// This plugin is used only on mobile, to highlight search results.
|
||||
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
|
||||
const stringUtils = require('../../stringUtils.js');
|
||||
const md5 = require('md5');
|
||||
|
||||
function createHighlightedTokens(Token, splitted) {
|
||||
function createHighlightedTokens(Token:any, splitted:string[]) {
|
||||
let token;
|
||||
const output = [];
|
||||
|
||||
@ -30,10 +34,11 @@ function createHighlightedTokens(Token, splitted) {
|
||||
return output;
|
||||
}
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
// function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
function plugin(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
const divider = md5(Date.now().toString() + Math.random().toString());
|
||||
|
||||
markdownIt.core.ruler.push('highlight_keywords', state => {
|
||||
markdownIt.core.ruler.push('highlight_keywords', (state:any) => {
|
||||
const keywords = ruleOptions.highlightedKeywords;
|
||||
if (!keywords || !keywords.length) return;
|
||||
|
||||
@ -60,8 +65,6 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions);
|
||||
};
|
||||
};
|
||||
export default {
|
||||
plugin,
|
||||
}
|
@ -1,39 +1,40 @@
|
||||
// const Resource = require('lib/models/Resource.js');
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
|
||||
const htmlUtils = require('../../htmlUtils.js');
|
||||
const utils = require('../../utils');
|
||||
|
||||
function renderImageHtml(before, src, after, ruleOptions) {
|
||||
function renderImageHtml(before:string, src:string, after:string, ruleOptions:RuleOptions) {
|
||||
const r = utils.imageReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
|
||||
if (typeof r === 'string') return r;
|
||||
if (r) return `<img ${before} ${htmlUtils.attributesHtml(r)} ${after}/>`;
|
||||
return `[Image: ${src}]`;
|
||||
}
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
function plugin(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
const Resource = ruleOptions.ResourceModel;
|
||||
|
||||
const htmlBlockDefaultRender =
|
||||
markdownIt.renderer.rules.html_block ||
|
||||
function(tokens, idx, options, env, self) {
|
||||
function(tokens:any[], idx:number, options:any, _env:any, self:any) {
|
||||
return self.renderToken(tokens, idx, options);
|
||||
};
|
||||
|
||||
const htmlInlineDefaultRender =
|
||||
markdownIt.renderer.rules.html_inline ||
|
||||
function(tokens, idx, options, env, self) {
|
||||
function(tokens:any[], idx:number, options:any, _env:any, self:any) {
|
||||
return self.renderToken(tokens, idx, options);
|
||||
};
|
||||
|
||||
const imageRegex = /<img(.*?)src=["'](.*?)["'](.*?)>/gi;
|
||||
|
||||
const handleImageTags = function(defaultRender) {
|
||||
return function(tokens, idx, options, env, self) {
|
||||
const handleImageTags = function(defaultRender:Function) {
|
||||
return function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
||||
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) => {
|
||||
return content.replace(imageRegex, (_v:any, before:string, src:string, after:string) => {
|
||||
if (!Resource.isResourceUrl(src)) return `<img${before}src="${src}"${after}>`;
|
||||
return renderImageHtml(before, src, after, ruleOptions);
|
||||
});
|
||||
@ -46,8 +47,4 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
markdownIt.renderer.rules.html_inline = handleImageTags(htmlInlineDefaultRender);
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions);
|
||||
};
|
||||
};
|
||||
export default { plugin }
|
@ -1,11 +1,13 @@
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
|
||||
// const Resource = require('lib/models/Resource.js');
|
||||
const utils = require('../../utils');
|
||||
const htmlUtils = require('../../htmlUtils.js');
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
function plugin(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
const defaultRender = markdownIt.renderer.rules.image;
|
||||
|
||||
markdownIt.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||
markdownIt.renderer.rules.image = (tokens:any[], idx:number, options:any, env:any, self:any) => {
|
||||
const Resource = ruleOptions.ResourceModel;
|
||||
|
||||
const token = tokens[idx];
|
||||
@ -19,12 +21,11 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
if (r) {
|
||||
let js = '';
|
||||
if (ruleOptions.enableLongPress) {
|
||||
const longPressDelay = ruleOptions.longPressDelay ? ruleOptions.longPressDelay : 500;
|
||||
const id = r['data-resource-id'];
|
||||
|
||||
const longPressHandler = `${ruleOptions.postMessageSyntax}('longclick:${id}')`;
|
||||
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${longPressHandler};}, ${longPressDelay});`;
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${longPressHandler};}, ${ruleOptions.longPressDelay});`;
|
||||
const touchEnd = 'if (!!t) clearTimeout(t); t=null';
|
||||
|
||||
js = ` ontouchstart="${touchStart}" ontouchend="${touchEnd}"`;
|
||||
@ -36,8 +37,4 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions);
|
||||
};
|
||||
};
|
||||
export default { plugin };
|
@ -1,8 +1,4 @@
|
||||
/* eslint prefer-const: 0*/
|
||||
|
||||
// Based on https://github.com/waylonflinn/markdown-it-katex
|
||||
|
||||
'use strict';
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
|
||||
let katex = require('katex');
|
||||
const md5 = require('md5');
|
||||
@ -46,7 +42,7 @@ function katexStyle() {
|
||||
|
||||
// Test if potential opening or closing delimieter
|
||||
// Assumes that there is a "$" at state.src[pos]
|
||||
function isValidDelim(state, pos) {
|
||||
function isValidDelim(state:any, pos:number) {
|
||||
let prevChar,
|
||||
nextChar,
|
||||
max = state.posMax,
|
||||
@ -71,7 +67,7 @@ function isValidDelim(state, pos) {
|
||||
};
|
||||
}
|
||||
|
||||
function math_inline(state, silent) {
|
||||
function math_inline(state:any, silent:boolean) {
|
||||
let start, match, token, res, pos;
|
||||
|
||||
if (state.src[state.pos] !== '$') {
|
||||
@ -146,7 +142,7 @@ function math_inline(state, silent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function math_block(state, start, end, silent) {
|
||||
function math_block(state:any, start:number, end:number, silent:boolean) {
|
||||
let firstLine,
|
||||
lastLine,
|
||||
next,
|
||||
@ -212,80 +208,71 @@ function math_block(state, start, end, silent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const cache_ = {};
|
||||
const cache_:any = {};
|
||||
|
||||
module.exports = {
|
||||
install: function(context) {
|
||||
function renderToStringWithCache(latex:string, katexOptions:any) {
|
||||
const cacheKey = md5(escape(latex) + escape(stringifySafe(katexOptions)));
|
||||
if (cacheKey in cache_) {
|
||||
return cache_[cacheKey];
|
||||
} else {
|
||||
const beforeMacros = stringifySafe(katexOptions.macros);
|
||||
const output = katex.renderToString(latex, katexOptions);
|
||||
const afterMacros = stringifySafe(katexOptions.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;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
plugin: function(markdownIt:any, options:RuleOptions) {
|
||||
// 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: {} };
|
||||
if (!options.context.userData.__katex) options.context.userData.__katex = { macros: {} };
|
||||
|
||||
const addContextAssets = () => {
|
||||
context.pluginAssets['katex'] = katexStyle();
|
||||
};
|
||||
const katexOptions:any = {}
|
||||
katexOptions.macros = options.context.userData.__katex.macros;
|
||||
katexOptions.trust = true;
|
||||
|
||||
function renderToStringWithCache(latex, options) {
|
||||
const cacheKey = md5(escape(latex) + escape(stringifySafe(options)));
|
||||
if (cacheKey in cache_) {
|
||||
return cache_[cacheKey];
|
||||
} else {
|
||||
const beforeMacros = stringifySafe(options.macros);
|
||||
const output = katex.renderToString(latex, options);
|
||||
const afterMacros = stringifySafe(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;
|
||||
// set KaTeX as the renderer for markdown-it-simplemath
|
||||
const katexInline = function(latex:string) {
|
||||
katexOptions.displayMode = false;
|
||||
try {
|
||||
return `<span class="joplin-editable"><span class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$" data-joplin-source-close="$">${markdownIt.utils.escapeHtml(latex)}</span>${renderToStringWithCache(latex, katexOptions)}</span>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
}
|
||||
|
||||
return function(md, options) {
|
||||
// Default options
|
||||
|
||||
options = options || {};
|
||||
options.macros = context.__katex.macros;
|
||||
options.trust = true;
|
||||
|
||||
// set KaTeX as the renderer for markdown-it-simplemath
|
||||
const katexInline = function(latex) {
|
||||
options.displayMode = false;
|
||||
try {
|
||||
return `<span class="joplin-editable"><span class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$" data-joplin-source-close="$">${md.utils.escapeHtml(latex)}</span>${renderToStringWithCache(latex, options)}</span>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
};
|
||||
|
||||
const inlineRenderer = function(tokens, idx) {
|
||||
addContextAssets();
|
||||
return katexInline(tokens[idx].content);
|
||||
};
|
||||
|
||||
const katexBlock = function(latex) {
|
||||
options.displayMode = true;
|
||||
try {
|
||||
return `<div class="joplin-editable"><pre class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$$ " data-joplin-source-close=" $$ ">${md.utils.escapeHtml(latex)}</pre>${renderToStringWithCache(latex, options)}</div>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
};
|
||||
|
||||
const 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;
|
||||
};
|
||||
|
||||
const inlineRenderer = function(tokens:any[], idx:number) {
|
||||
return katexInline(tokens[idx].content);
|
||||
};
|
||||
|
||||
const katexBlock = function(latex:string) {
|
||||
katexOptions.displayMode = true;
|
||||
try {
|
||||
return `<div class="joplin-editable"><pre class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$$ " data-joplin-source-close=" $$ ">${markdownIt.utils.escapeHtml(latex)}</pre>${renderToStringWithCache(latex, katexOptions)}</div>`;
|
||||
} catch (error) {
|
||||
console.error('Katex error for:', latex, error);
|
||||
return latex;
|
||||
}
|
||||
};
|
||||
|
||||
const blockRenderer = function(tokens:any[], idx:number) {
|
||||
return `${katexBlock(tokens[idx].content)}\n`;
|
||||
};
|
||||
|
||||
markdownIt.inline.ruler.after('escape', 'math_inline', math_inline);
|
||||
markdownIt.block.ruler.after('blockquote', 'math_block', math_block, {
|
||||
alt: ['paragraph', 'reference', 'blockquote', 'list'],
|
||||
});
|
||||
markdownIt.renderer.rules.math_inline = inlineRenderer;
|
||||
markdownIt.renderer.rules.math_block = blockRenderer;
|
||||
},
|
||||
|
||||
assets: katexStyle,
|
||||
};
|
@ -1,18 +1,13 @@
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
const utils = require('../../utils');
|
||||
const urlUtils = require('../../urlUtils.js');
|
||||
const { getClassNameForMimeType } = require('font-awesome-filetypes');
|
||||
|
||||
function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
const pluginOptions = {
|
||||
// linkRenderingType = 1 is the regular rendering and clicking on it is handled via embedded JS (in onclick attribute)
|
||||
// linkRenderingType = 2 gives a plain link with no JS. Caller needs to handle clicking on the link.
|
||||
linkRenderingType: 1,
|
||||
...ruleOptions.plugins['link_open'],
|
||||
};
|
||||
|
||||
markdownIt.renderer.rules.link_open = function(tokens, idx) {
|
||||
function plugin(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
markdownIt.renderer.rules.link_open = function(tokens:any[], idx:number) {
|
||||
const token = tokens[idx];
|
||||
let href = utils.getAttr(token.attrs, 'href');
|
||||
const resourceHrefInfo = urlUtils.parseResourceUrl(href);
|
||||
@ -65,12 +60,10 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
|
||||
let js = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)}, { resourceId: ${JSON.stringify(resourceId)} }); return false;`;
|
||||
if (ruleOptions.enableLongPress && !!resourceId) {
|
||||
const longPressDelay = ruleOptions.longPressDelay ? ruleOptions.longPressDelay : 500;
|
||||
|
||||
const onClick = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)})`;
|
||||
const onLongClick = `${ruleOptions.postMessageSyntax}("longclick:${resourceId}")`;
|
||||
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${onLongClick};}, ${longPressDelay});`;
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${onLongClick};}, ${ruleOptions.longPressDelay});`;
|
||||
const touchEnd = `if (!!t) {clearTimeout(t); t=null; ${onClick};}`;
|
||||
|
||||
js = `ontouchstart='${touchStart}' ontouchend='${touchEnd}'`;
|
||||
@ -80,7 +73,7 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
|
||||
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 || pluginOptions.linkRenderingType === 2) {
|
||||
if (ruleOptions.plainResourceRendering || ruleOptions.linkRenderingType === 2) {
|
||||
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${htmlentities(href)}' type='${htmlentities(mime)}'>`;
|
||||
} else {
|
||||
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${hrefAttr}' ${js} type='${htmlentities(mime)}'>${icon}`;
|
||||
@ -88,9 +81,4 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(context, ruleOptions) {
|
||||
|
||||
return function(md, mdOptions) {
|
||||
installRule(md, mdOptions, ruleOptions);
|
||||
};
|
||||
};
|
||||
export default { plugin };
|
@ -1,49 +1,35 @@
|
||||
function style() {
|
||||
return [
|
||||
{ name: 'mermaid.min.js' },
|
||||
{ name: 'mermaid_render.js' },
|
||||
{
|
||||
inline: true,
|
||||
// Note: Mermaid is buggy when rendering below a certain width (500px?)
|
||||
// so set an arbitrarily high width here for the container. Once the
|
||||
// diagram is rendered it will be reset to 100% in mermaid_render.js
|
||||
text: '.mermaid { background-color: white; width: 640px; }',
|
||||
mime: 'text/css',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function addContextAssets(context:any) {
|
||||
if ('mermaid' in context.pluginAssets) return;
|
||||
|
||||
context.pluginAssets['mermaid'] = style();
|
||||
}
|
||||
|
||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
||||
const defaultRender:Function = markdownIt.renderer.rules.fence || function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
|
||||
markdownIt.renderer.rules.fence = function(tokens:any[], idx:number, options:{}, env:any, self:any) {
|
||||
const token = tokens[idx];
|
||||
if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self);
|
||||
addContextAssets(context);
|
||||
const contentHtml = markdownIt.utils.escapeHtml(token.content);
|
||||
return `
|
||||
<div class="joplin-editable">
|
||||
<pre class="joplin-source" data-joplin-language="mermaid" data-joplin-source-open="\`\`\`mermaid " data-joplin-source-close=" \`\`\` ">${contentHtml}</pre>
|
||||
<div class="mermaid">${contentHtml}</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
install: function(context:any, ruleOptions:any) {
|
||||
return function(md:any, mdOptions:any) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
|
||||
assets: function() {
|
||||
return [
|
||||
{ name: 'mermaid.min.js' },
|
||||
{ name: 'mermaid_render.js' },
|
||||
{
|
||||
inline: true,
|
||||
// Note: Mermaid is buggy when rendering below a certain width (500px?)
|
||||
// so set an arbitrarily high width here for the container. Once the
|
||||
// diagram is rendered it will be reset to 100% in mermaid_render.js
|
||||
text: '.mermaid { background-color: white; width: 640px; }',
|
||||
mime: 'text/css',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
plugin: function(markdownIt:any) {
|
||||
const defaultRender:Function = markdownIt.renderer.rules.fence || function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
|
||||
markdownIt.renderer.rules.fence = function(tokens:any[], idx:number, options:{}, env:any, self:any) {
|
||||
const token = tokens[idx];
|
||||
if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self);
|
||||
const contentHtml = markdownIt.utils.escapeHtml(token.content);
|
||||
return `
|
||||
<div class="joplin-editable">
|
||||
<pre class="joplin-source" data-joplin-language="mermaid" data-joplin-source-open="\`\`\`mermaid " data-joplin-source-close=" \`\`\` ">${contentHtml}</pre>
|
||||
<div class="mermaid">${contentHtml}</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
},
|
||||
assets: style,
|
||||
};
|
||||
|
@ -1,53 +1,50 @@
|
||||
import { RuleOptions } from 'lib/joplin-renderer/MdToHtml';
|
||||
|
||||
const md5 = require('md5');
|
||||
const htmlUtils = require('../../htmlUtils');
|
||||
|
||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
||||
markdownIt.core.ruler.push('sanitize_html', (state:any) => {
|
||||
const tokens = state.tokens;
|
||||
export default {
|
||||
plugin: function(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
markdownIt.core.ruler.push('sanitize_html', (state:any) => {
|
||||
const tokens = state.tokens;
|
||||
|
||||
const walkHtmlTokens = (tokens:any[]) => {
|
||||
if (!tokens || !tokens.length) return;
|
||||
const walkHtmlTokens = (tokens:any[]) => {
|
||||
if (!tokens || !tokens.length) return;
|
||||
|
||||
for (const token of tokens) {
|
||||
if (!['html_block', 'html_inline'].includes(token.type)) {
|
||||
for (const token of tokens) {
|
||||
if (!['html_block', 'html_inline'].includes(token.type)) {
|
||||
walkHtmlTokens(token.children);
|
||||
continue;
|
||||
}
|
||||
|
||||
const cacheKey = md5(escape(token.content));
|
||||
let sanitizedContent = ruleOptions.context.cache.value(cacheKey);
|
||||
|
||||
// For html_inline, the content is only a fragment of HTML, as it will be rendered, but
|
||||
// it's not necessarily valid HTML. For example this HTML:
|
||||
//
|
||||
// <a href="#">Testing</a>
|
||||
//
|
||||
// will be rendered as three tokens:
|
||||
//
|
||||
// html_inline: <a href="#">
|
||||
// text: Testing
|
||||
// html_inline: </a>
|
||||
//
|
||||
// So the sanitizeHtml function must handle this kind of non-valid HTML.
|
||||
|
||||
if (!sanitizedContent) {
|
||||
sanitizedContent = htmlUtils.sanitizeHtml(token.content, { addNoMdConvClass: true });
|
||||
}
|
||||
|
||||
token.content = sanitizedContent;
|
||||
|
||||
ruleOptions.context.cache.setValue(cacheKey, sanitizedContent, 1000 * 60 * 60);
|
||||
walkHtmlTokens(token.children);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
const cacheKey = md5(escape(token.content));
|
||||
let sanitizedContent = context.cache.value(cacheKey);
|
||||
|
||||
// For html_inline, the content is only a fragment of HTML, as it will be rendered, but
|
||||
// it's not necessarily valid HTML. For example this HTML:
|
||||
//
|
||||
// <a href="#">Testing</a>
|
||||
//
|
||||
// will be rendered as three tokens:
|
||||
//
|
||||
// html_inline: <a href="#">
|
||||
// text: Testing
|
||||
// html_inline: </a>
|
||||
//
|
||||
// So the sanitizeHtml function must handle this kind of non-valid HTML.
|
||||
|
||||
if (!sanitizedContent) {
|
||||
sanitizedContent = htmlUtils.sanitizeHtml(token.content, { addNoMdConvClass: true });
|
||||
}
|
||||
|
||||
token.content = sanitizedContent;
|
||||
|
||||
context.cache.setValue(cacheKey, sanitizedContent, 1000 * 60 * 60);
|
||||
walkHtmlTokens(token.children);
|
||||
}
|
||||
};
|
||||
|
||||
walkHtmlTokens(tokens);
|
||||
});
|
||||
}
|
||||
|
||||
export default function(context:any, ruleOptions:any) {
|
||||
return function(md:any, mdOptions:any) {
|
||||
installRule(md, mdOptions, ruleOptions, context);
|
||||
};
|
||||
}
|
||||
walkHtmlTokens(tokens);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -322,20 +322,36 @@ export enum ContentScriptType {
|
||||
* Registers a new Markdown-It plugin, which should follow this template:
|
||||
*
|
||||
* ```javascript
|
||||
* // The module should export a function that takes a `pluginContext` as argument (currently unused)
|
||||
* module.exports = function(pluginContext) {
|
||||
* // That function should return an object with a number of properties:
|
||||
* return {
|
||||
* // Required:
|
||||
* install: function(context, ruleOptions) {
|
||||
* return function(md, mdOptions) {
|
||||
* installRule(md, mdOptions, ruleOptions, context);
|
||||
* };
|
||||
*
|
||||
* // The module should export an object like below:
|
||||
*
|
||||
* module.exports = {
|
||||
* default: {
|
||||
* // This is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information
|
||||
* // The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml.ts), which
|
||||
* // contains a number of options, mostly useful for Joplin's internal code.
|
||||
* plugin: function(markdownIt, options) {
|
||||
*
|
||||
* },
|
||||
*
|
||||
* // You may also specify additional assets such as JS or CSS that should be loaded in the rendered HTML document.
|
||||
* // Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to
|
||||
* // see how the data should be structured.
|
||||
* assets: {},
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific feature, you
|
||||
* would simply create a file such as this:
|
||||
*
|
||||
* ```javascript
|
||||
* module.exports = {
|
||||
* default: {
|
||||
* plugin: require('markdown-it-toc-done-right');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
MarkdownItPlugin = 'markdownItPlugin',
|
||||
CodeMirrorPlugin = 'codeMirrorPlugin',
|
||||
|
@ -1,8 +0,0 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const uuid_1 = require('lib/uuid');
|
||||
function viewIdGen(plugin) {
|
||||
return `plugin-view-${plugin.id}-${uuid_1.default.createNano()}`;
|
||||
}
|
||||
exports.default = viewIdGen;
|
||||
// # sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlld0lkR2VuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsidmlld0lkR2VuLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQ0EsbUNBQTRCO0FBRTVCLFNBQXdCLFNBQVMsQ0FBQyxNQUFhO0lBQzlDLE9BQU8sZUFBZSxNQUFNLENBQUMsRUFBRSxJQUFJLGNBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFBO0FBQ3ZELENBQUM7QUFGRCw0QkFFQyJ9
|
@ -393,6 +393,7 @@ function themeStyle(themeId:number) {
|
||||
output = Object.assign({}, globalStyle, fontSizes, themes[themeId]);
|
||||
output = addMissingProperties(output);
|
||||
output = addExtraStyles(output);
|
||||
output.cacheKey = cacheKey;
|
||||
|
||||
themeCache_[cacheKey] = output;
|
||||
return themeCache_[cacheKey];
|
||||
|
Loading…
Reference in New Issue
Block a user