(resolve => {
+ const openPath = async (url: string) => {
+ resolve(url);
+ return '';
+ };
+ shell.openPath = openPath;
+ });
+ });
+ await pdfLink.click();
+ expect(await nextOpenFilePromise).toMatch(/\.pdf$/);
+
+ // Should not have rendered something else in the viewer frame
+ await expectToBeRendered();
});
test('preview pane should render video attachments', async ({ mainWindow, electronApp }) => {
diff --git a/packages/app-mobile/commands/util/showResource.ts b/packages/app-mobile/commands/util/showResource.ts
index cda52e0f4..b10cc3b95 100644
--- a/packages/app-mobile/commands/util/showResource.ts
+++ b/packages/app-mobile/commands/util/showResource.ts
@@ -14,7 +14,7 @@ const showResource = async (item: ResourceEntity) => {
if (shim.mobilePlatform() === 'web') {
const url = URL.createObjectURL(await shim.fsDriver().fileAtPath(resourcePath));
const w = window.open(url, '_blank');
- w.addEventListener('close', () => {
+ w?.addEventListener('close', () => {
URL.revokeObjectURL(url);
}, { once: true });
} else {
diff --git a/packages/lib/services/interop/InteropService_Exporter_Html.ts b/packages/lib/services/interop/InteropService_Exporter_Html.ts
index 8cb2b4fff..e8aa08e62 100644
--- a/packages/lib/services/interop/InteropService_Exporter_Html.ts
+++ b/packages/lib/services/interop/InteropService_Exporter_Html.ts
@@ -14,6 +14,7 @@ const { themeStyle } = require('../../theme');
const { escapeHtml } = require('../../string-utils.js');
import { assetsToHeaders } from '@joplin/renderer';
import getPluginSettingValue from '../plugins/utils/getPluginSettingValue';
+import { LinkRenderingType } from '@joplin/renderer/MdToHtml';
export default class InteropService_Exporter_Html extends InteropService_Exporter_Base {
@@ -115,8 +116,14 @@ export default class InteropService_Exporter_Html extends InteropService_Exporte
const bodyMd = await this.processNoteResources_(item);
const result = await this.markupToHtml_.render(item.markup_language, bodyMd, this.style_, {
resources: this.resources_,
- plainResourceRendering: true,
settingValue: getPluginSettingValue,
+
+ plainResourceRendering: true,
+ plugins: {
+ link_open: {
+ linkRenderingType: LinkRenderingType.HrefHandler,
+ },
+ },
});
const noteContent = [];
if (item.title) noteContent.push(`${escapeHtml(item.title)}
`);
diff --git a/packages/renderer/MdToHtml.ts b/packages/renderer/MdToHtml.ts
index 3170e2193..d069024fb 100644
--- a/packages/renderer/MdToHtml.ts
+++ b/packages/renderer/MdToHtml.ts
@@ -146,6 +146,14 @@ interface PluginContext {
};
}
+export enum LinkRenderingType {
+ // linkRenderingType = 1 is the regular rendering and clicking on it is handled via embedded JS (in onclick attribute)
+ JavaScriptHandler = 1,
+
+ // linkRenderingType = 2 gives a plain link with no JS. Caller needs to handle clicking on the link.
+ HrefHandler = 2,
+}
+
export interface RuleOptions {
context: PluginContext;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -174,9 +182,7 @@ export interface RuleOptions {
enableLongPress?: boolean;
// 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;
+ linkRenderingType?: LinkRenderingType;
// A list of MIME types for which an edit button appears on tap/hover.
// Used by the image editor in the mobile app.
diff --git a/packages/renderer/MdToHtml/linkReplacement.ts b/packages/renderer/MdToHtml/linkReplacement.ts
index b8f025a4f..ca48ee17e 100644
--- a/packages/renderer/MdToHtml/linkReplacement.ts
+++ b/packages/renderer/MdToHtml/linkReplacement.ts
@@ -1,3 +1,4 @@
+import { LinkRenderingType } from '../MdToHtml';
import { ItemIdToUrlHandler, OptionsResourceModel } from '../types';
import * as utils from '../utils';
import createEventHandlingAttrs from './createEventHandlingAttrs';
@@ -11,7 +12,7 @@ export interface Options {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
resources?: any;
ResourceModel?: OptionsResourceModel;
- linkRenderingType?: number;
+ linkRenderingType?: LinkRenderingType;
plainResourceRendering?: boolean;
postMessageSyntax?: string;
enableLongPress?: boolean;
@@ -27,16 +28,14 @@ export interface LinkReplacementResult {
}
export default function(href: string, options: Options = null): LinkReplacementResult {
- options = {
- title: '',
- resources: {},
- ResourceModel: null,
- linkRenderingType: 1,
- plainResourceRendering: false,
- postMessageSyntax: 'postMessage',
- enableLongPress: false,
- ...options,
- };
+ options = { ...options };
+ options.title ??= '';
+ options.resources ??= {};
+ options.ResourceModel ??= null;
+ options.linkRenderingType ??= LinkRenderingType.JavaScriptHandler;
+ options.plainResourceRendering ??= false;
+ options.postMessageSyntax ??= 'postMessage';
+ options.enableLongPress ??= false;
const resourceHrefInfo = urlUtils.parseResourceUrl(href);
const isResourceUrl = options.resources && !!resourceHrefInfo;
@@ -129,12 +128,15 @@ export default function(href: string, options: Options = null): LinkReplacementR
if (addedHrefAttr) {
// Done -- the HREF has already bee set.
- } else if (options.plainResourceRendering || options.linkRenderingType === 2) {
+ } else if (options.plainResourceRendering || options.linkRenderingType === LinkRenderingType.HrefHandler) {
icon = '';
attrHtml.push(`href='${htmlentities(href)}'`);
} else {
attrHtml.push(`href='${htmlentities(hrefAttr)}'`);
- if (js) attrHtml.push(js);
+ }
+
+ if (js && options.linkRenderingType === LinkRenderingType.JavaScriptHandler) {
+ attrHtml.push(js);
}
return {
diff --git a/packages/renderer/MdToHtml/rules/link_close.ts b/packages/renderer/MdToHtml/rules/link_close.ts
index 0ca17e4cd..9bdc2ecb6 100644
--- a/packages/renderer/MdToHtml/rules/link_close.ts
+++ b/packages/renderer/MdToHtml/rules/link_close.ts
@@ -1,7 +1,7 @@
// This rule is used to add a media player for certain resource types below
// the link.
-import { RuleOptions } from '../../MdToHtml';
+import { LinkRenderingType, RuleOptions } from '../../MdToHtml';
import renderMedia, { Options as RenderMediaOptions } from '../renderMedia';
@@ -23,7 +23,7 @@ function plugin(markdownIt: any, ruleOptions: RuleOptions) {
const defaultOutput = defaultRender(tokens, idx, options, env, self);
const link = ruleOptions.context.currentLinks.pop();
- if (!link || ruleOptions.linkRenderingType === 2 || ruleOptions.plainResourceRendering) return defaultOutput;
+ if (!link || ruleOptions.linkRenderingType === LinkRenderingType.HrefHandler || ruleOptions.plainResourceRendering) return defaultOutput;
return [defaultOutput, renderMedia(link, ruleOptions as RenderMediaOptions, linkIndexes)].join('');
};