mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Merge branch 'dev' of https://github.com/laurent22/joplin into dev
This commit is contained in:
commit
86bace70a5
@ -1351,6 +1351,9 @@ packages/lib/uuid.js.map
|
||||
packages/lib/versionInfo.d.ts
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.js.map
|
||||
packages/renderer/HtmlToHtml.d.ts
|
||||
packages/renderer/HtmlToHtml.js
|
||||
packages/renderer/HtmlToHtml.js.map
|
||||
packages/renderer/InMemoryCache.d.ts
|
||||
packages/renderer/InMemoryCache.js
|
||||
packages/renderer/InMemoryCache.js.map
|
||||
@ -1360,6 +1363,12 @@ packages/renderer/MarkupToHtml.js.map
|
||||
packages/renderer/MdToHtml.d.ts
|
||||
packages/renderer/MdToHtml.js
|
||||
packages/renderer/MdToHtml.js.map
|
||||
packages/renderer/MdToHtml/linkReplacement.d.ts
|
||||
packages/renderer/MdToHtml/linkReplacement.js
|
||||
packages/renderer/MdToHtml/linkReplacement.js.map
|
||||
packages/renderer/MdToHtml/linkReplacement.test.d.ts
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js.map
|
||||
packages/renderer/MdToHtml/rules/checkbox.d.ts
|
||||
packages/renderer/MdToHtml/rules/checkbox.js
|
||||
packages/renderer/MdToHtml/rules/checkbox.js.map
|
||||
@ -1393,6 +1402,9 @@ packages/renderer/MdToHtml/rules/mermaid.js.map
|
||||
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
|
||||
packages/renderer/MdToHtml/rules/sanitize_html.js
|
||||
packages/renderer/MdToHtml/rules/sanitize_html.js.map
|
||||
packages/renderer/htmlUtils.d.ts
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/htmlUtils.js.map
|
||||
packages/renderer/index.d.ts
|
||||
packages/renderer/index.js
|
||||
packages/renderer/index.js.map
|
||||
@ -1402,4 +1414,7 @@ packages/renderer/noteStyle.js.map
|
||||
packages/renderer/pathUtils.d.ts
|
||||
packages/renderer/pathUtils.js
|
||||
packages/renderer/pathUtils.js.map
|
||||
packages/renderer/utils.d.ts
|
||||
packages/renderer/utils.js
|
||||
packages/renderer/utils.js.map
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
|
15
.gitignore
vendored
15
.gitignore
vendored
@ -1343,6 +1343,9 @@ packages/lib/uuid.js.map
|
||||
packages/lib/versionInfo.d.ts
|
||||
packages/lib/versionInfo.js
|
||||
packages/lib/versionInfo.js.map
|
||||
packages/renderer/HtmlToHtml.d.ts
|
||||
packages/renderer/HtmlToHtml.js
|
||||
packages/renderer/HtmlToHtml.js.map
|
||||
packages/renderer/InMemoryCache.d.ts
|
||||
packages/renderer/InMemoryCache.js
|
||||
packages/renderer/InMemoryCache.js.map
|
||||
@ -1352,6 +1355,12 @@ packages/renderer/MarkupToHtml.js.map
|
||||
packages/renderer/MdToHtml.d.ts
|
||||
packages/renderer/MdToHtml.js
|
||||
packages/renderer/MdToHtml.js.map
|
||||
packages/renderer/MdToHtml/linkReplacement.d.ts
|
||||
packages/renderer/MdToHtml/linkReplacement.js
|
||||
packages/renderer/MdToHtml/linkReplacement.js.map
|
||||
packages/renderer/MdToHtml/linkReplacement.test.d.ts
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js.map
|
||||
packages/renderer/MdToHtml/rules/checkbox.d.ts
|
||||
packages/renderer/MdToHtml/rules/checkbox.js
|
||||
packages/renderer/MdToHtml/rules/checkbox.js.map
|
||||
@ -1385,6 +1394,9 @@ packages/renderer/MdToHtml/rules/mermaid.js.map
|
||||
packages/renderer/MdToHtml/rules/sanitize_html.d.ts
|
||||
packages/renderer/MdToHtml/rules/sanitize_html.js
|
||||
packages/renderer/MdToHtml/rules/sanitize_html.js.map
|
||||
packages/renderer/htmlUtils.d.ts
|
||||
packages/renderer/htmlUtils.js
|
||||
packages/renderer/htmlUtils.js.map
|
||||
packages/renderer/index.d.ts
|
||||
packages/renderer/index.js
|
||||
packages/renderer/index.js.map
|
||||
@ -1394,4 +1406,7 @@ packages/renderer/noteStyle.js.map
|
||||
packages/renderer/pathUtils.d.ts
|
||||
packages/renderer/pathUtils.js
|
||||
packages/renderer/pathUtils.js.map
|
||||
packages/renderer/utils.d.ts
|
||||
packages/renderer/utils.js
|
||||
packages/renderer/utils.js.map
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
@ -9,7 +9,7 @@ const Folder = require('@joplin/lib/models/Folder.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const HtmlToHtml = require('@joplin/renderer/HtmlToHtml');
|
||||
const HtmlToHtml = require('@joplin/renderer/HtmlToHtml').default;
|
||||
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
@ -208,6 +208,13 @@ export enum MenuItemLocation {
|
||||
* @deprecated Do not use - same as NoteListContextMenu
|
||||
*/
|
||||
Context = 'context',
|
||||
|
||||
/**
|
||||
* The context menu that appears when right-clicking on the note
|
||||
* list, or when multiple notes are selected. Any command triggered from
|
||||
* this location will receive a `noteIds` array with the list of notes that
|
||||
* were right-clicked or selected.
|
||||
*/
|
||||
NoteListContextMenu = 'noteListContextMenu',
|
||||
EditorContextMenu = 'editorContextMenu',
|
||||
}
|
||||
|
@ -21,6 +21,20 @@ joplin.plugins.register({
|
||||
},
|
||||
});
|
||||
|
||||
await joplin.commands.register({
|
||||
name: 'contextMenuCommandExample',
|
||||
label: 'My Context Menu command',
|
||||
execute: async (noteIds:string[]) => {
|
||||
const notes = [];
|
||||
for (const noteId of noteIds) {
|
||||
notes.push(await joplin.data.get(['notes', noteId]));
|
||||
}
|
||||
|
||||
const noteTitles = notes.map((note:any) => note.title);
|
||||
alert('The following notes will be processed:\n\n' + noteTitles.join(', '));
|
||||
},
|
||||
});
|
||||
|
||||
// Commands that return a result and take argument can only be used
|
||||
// programmatically, so it's not necessary to set a label and icon.
|
||||
await joplin.commands.register({
|
||||
@ -40,6 +54,8 @@ joplin.plugins.register({
|
||||
await joplin.views.menuItems.create('myMenuItem1', 'testCommand1', MenuItemLocation.Tools, { accelerator: 'CmdOrCtrl+Alt+Shift+B' });
|
||||
await joplin.views.menuItems.create('myMenuItem2', 'testCommand2', MenuItemLocation.Tools);
|
||||
|
||||
await joplin.views.menuItems.create('contextMenuItem1', 'contextMenuCommandExample', MenuItemLocation.NoteListContextMenu);
|
||||
|
||||
console.info('Running command with arguments...');
|
||||
const result = await joplin.commands.execute('commandWithResult', 'abcd', 123);
|
||||
console.info('Result was: ' + result);
|
||||
|
@ -3,15 +3,16 @@ import { FormNote, defaultFormNote, ResourceInfos } from './types';
|
||||
import { clearResourceCache, attachedResources } from './resourceHandling';
|
||||
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||
import { handleResourceDownloadMode } from './resourceHandling';
|
||||
import HtmlToHtml from '@joplin/renderer/HtmlToHtml';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import usePrevious from '../../hooks/usePrevious';
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index';
|
||||
|
||||
const { MarkupToHtml } = require('@joplin/renderer');
|
||||
const HtmlToHtml = require('@joplin/renderer/HtmlToHtml');
|
||||
const usePrevious = require('../../hooks/usePrevious').default;
|
||||
const Note = require('@joplin/lib/models/Note');
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js');
|
||||
const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker.js');
|
||||
const ResourceEditWatcher = require('@joplin/lib/services/ResourceEditWatcher/index').default;
|
||||
|
||||
export interface OnLoadEvent {
|
||||
formNote: FormNote;
|
||||
|
@ -186,7 +186,7 @@ export default class NoteListUtils {
|
||||
if (location !== MenuItemLocation.Context && location !== MenuItemLocation.NoteListContextMenu) continue;
|
||||
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem(info.view.commandName))
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem(info.view.commandName, noteIds))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,8 @@
|
||||
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
|
||||
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json",
|
||||
"start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"test-ci": "test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -87,7 +87,6 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
||||
codeTheme: theme.codeThemeCss,
|
||||
postMessageSyntax: 'window.joplinPostMessage_',
|
||||
enableLongPress: shim.mobilePlatform() === 'android', // On iOS, there's already a built-on open/share menu
|
||||
longPressDelay: 500, // TODO use system value
|
||||
};
|
||||
|
||||
// Whenever a resource state changes, for example when it goes from "not downloaded" to "downloaded", the "noteResources"
|
||||
|
@ -1,6 +1,6 @@
|
||||
const htmlUtils = require('./htmlUtils');
|
||||
const utils = require('./utils');
|
||||
// const noteStyle = require('./noteStyle').default;
|
||||
import htmlUtils from './htmlUtils';
|
||||
import linkReplacement from './MdToHtml/linkReplacement';
|
||||
import utils from './utils';
|
||||
|
||||
// TODO: fix
|
||||
// const Setting = require('@joplin/lib/models/Setting').default;
|
||||
@ -13,9 +13,45 @@ const md5 = require('md5');
|
||||
// relatively small.
|
||||
const inMemoryCache = new InMemoryCache(10);
|
||||
|
||||
class HtmlToHtml {
|
||||
constructor(options) {
|
||||
if (!options) options = {};
|
||||
interface FsDriver {
|
||||
writeFile: Function;
|
||||
exists: Function;
|
||||
cacheCssToFile: Function;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
ResourceModel: any;
|
||||
resourceBaseUrl?: string;
|
||||
fsDriver?: FsDriver;
|
||||
}
|
||||
|
||||
interface RenderOptions {
|
||||
splitted: boolean;
|
||||
bodyOnly: boolean;
|
||||
externalAssetsOnly: boolean;
|
||||
resources: any;
|
||||
postMessageSyntax: string;
|
||||
enableLongPress: boolean;
|
||||
}
|
||||
|
||||
interface RenderResult {
|
||||
html: string;
|
||||
pluginAssets: any[];
|
||||
}
|
||||
|
||||
export default class HtmlToHtml {
|
||||
|
||||
private resourceBaseUrl_;
|
||||
private ResourceModel_;
|
||||
private cache_;
|
||||
private fsDriver_: any;
|
||||
|
||||
constructor(options: Options = null) {
|
||||
options = {
|
||||
ResourceModel: null,
|
||||
...options,
|
||||
};
|
||||
|
||||
this.resourceBaseUrl_ = 'resourceBaseUrl' in options ? options.resourceBaseUrl : null;
|
||||
this.ResourceModel_ = options.ResourceModel;
|
||||
this.cache_ = inMemoryCache;
|
||||
@ -36,7 +72,7 @@ class HtmlToHtml {
|
||||
return this.fsDriver_;
|
||||
}
|
||||
|
||||
splitHtml(html) {
|
||||
splitHtml(html: string) {
|
||||
const trimmedHtml = html.trimStart();
|
||||
if (trimmedHtml.indexOf('<style>') !== 0) return { html: html, css: '' };
|
||||
|
||||
@ -49,17 +85,20 @@ class HtmlToHtml {
|
||||
};
|
||||
}
|
||||
|
||||
async allAssets(/* theme*/) {
|
||||
async allAssets(/* theme*/): Promise<any[]> {
|
||||
return []; // TODO
|
||||
}
|
||||
|
||||
// Note: the "theme" variable is ignored and instead the light theme is
|
||||
// always used for HTML notes.
|
||||
// See: https://github.com/laurent22/joplin/issues/3698
|
||||
async render(markup, _theme, options) {
|
||||
options = Object.assign({}, {
|
||||
async render(markup: string, _theme: any, options: RenderOptions): Promise<RenderResult> {
|
||||
options = {
|
||||
splitted: false,
|
||||
}, options);
|
||||
postMessageSyntax: 'postMessage',
|
||||
enableLongPress: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
const cacheKey = md5(escape(markup));
|
||||
let html = this.cache_.value(cacheKey);
|
||||
@ -67,7 +106,7 @@ class HtmlToHtml {
|
||||
if (!html) {
|
||||
html = htmlUtils.sanitizeHtml(markup);
|
||||
|
||||
html = htmlUtils.processImageTags(html, data => {
|
||||
html = htmlUtils.processImageTags(html, (data: any) => {
|
||||
if (!data.src) return null;
|
||||
|
||||
const r = utils.imageReplacement(this.ResourceModel_, data.src, options.resources, this.resourceBaseUrl_);
|
||||
@ -85,6 +124,24 @@ class HtmlToHtml {
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
html = htmlUtils.processAnchorTags(html, (data: any) => {
|
||||
if (!data.href) return null;
|
||||
|
||||
const r = linkReplacement(data.href, {
|
||||
resources: options.resources,
|
||||
ResourceModel: this.ResourceModel_,
|
||||
postMessageSyntax: options.postMessageSyntax,
|
||||
enableLongPress: options.enableLongPress,
|
||||
});
|
||||
|
||||
if (!r) return null;
|
||||
|
||||
return {
|
||||
type: 'replaceElement',
|
||||
html: r,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
this.cache_.setValue(cacheKey, html, 1000 * 60 * 10);
|
||||
@ -98,13 +155,13 @@ class HtmlToHtml {
|
||||
|
||||
// const lightTheme = themeStyle(Setting.THEME_LIGHT);
|
||||
// let cssStrings = noteStyle(lightTheme);
|
||||
let cssStrings = [];
|
||||
let cssStrings: string[] = [];
|
||||
|
||||
if (options.splitted) {
|
||||
const splitted = this.splitHtml(html);
|
||||
cssStrings = [splitted.css].concat(cssStrings);
|
||||
|
||||
const output = {
|
||||
const output: RenderResult = {
|
||||
html: splitted.html,
|
||||
pluginAssets: [],
|
||||
};
|
||||
@ -124,5 +181,3 @@ class HtmlToHtml {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HtmlToHtml;
|
@ -1,6 +1,6 @@
|
||||
import MdToHtml from './MdToHtml';
|
||||
const HtmlToHtml = require('./HtmlToHtml');
|
||||
const htmlUtils = require('./htmlUtils');
|
||||
import HtmlToHtml from './HtmlToHtml';
|
||||
import htmlUtils from './htmlUtils';
|
||||
const MarkdownIt = require('markdown-it');
|
||||
|
||||
export enum MarkupLanguage {
|
||||
|
@ -138,10 +138,6 @@ export interface RuleOptions {
|
||||
// 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.
|
||||
|
53
packages/renderer/MdToHtml/linkReplacement.test.ts
Normal file
53
packages/renderer/MdToHtml/linkReplacement.test.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import linkReplacement from './linkReplacement';
|
||||
|
||||
describe('linkReplacement', () => {
|
||||
|
||||
test('should handle non-resource links', () => {
|
||||
const r = linkReplacement('https://example.com/test');
|
||||
expect(r).toBe('<a data-from-md href=\'https://example.com/test\' onclick=\'postMessage("https://example.com/test", { resourceId: "" }); return false;\'>');
|
||||
});
|
||||
|
||||
test('should handle non-resource links - simple rendering', () => {
|
||||
const r = linkReplacement('https://example.com/test', { linkRenderingType: 2 });
|
||||
expect(r).toBe('<a data-from-md href=\'https://example.com/test\'>');
|
||||
});
|
||||
|
||||
test('should handle resource links - downloaded status', () => {
|
||||
const resourceId = 'f6afba55bdf74568ac94f8d1e3578d2c';
|
||||
|
||||
const r = linkReplacement(`:/${resourceId}`, {
|
||||
ResourceModel: {},
|
||||
resources: {
|
||||
[resourceId]: {
|
||||
item: {},
|
||||
localState: {
|
||||
fetch_status: 2, // FETCH_STATUS_DONE
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(r).toBe(`<a data-from-md data-resource-id='${resourceId}' href='#' onclick='postMessage("joplin://${resourceId}", { resourceId: "${resourceId}" }); return false;'><span class="resource-icon fa-joplin"></span>`);
|
||||
});
|
||||
|
||||
test('should handle resource links - idle status', () => {
|
||||
const resourceId = 'f6afba55bdf74568ac94f8d1e3578d2c';
|
||||
|
||||
const r = linkReplacement(`:/${resourceId}`, {
|
||||
ResourceModel: {},
|
||||
resources: {
|
||||
[resourceId]: {
|
||||
item: {},
|
||||
localState: {
|
||||
fetch_status: 0, // FETCH_STATUS_IDLE
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Since the icon is embedded as SVG, we only check for the prefix
|
||||
const expectedPrefix = `<a class="not-loaded-resource resource-status-notDownloaded" data-resource-id="${resourceId}"><img src="data:image/svg+xml;utf8`;
|
||||
expect(r.indexOf(expectedPrefix)).toBe(0);
|
||||
});
|
||||
|
||||
});
|
104
packages/renderer/MdToHtml/linkReplacement.ts
Normal file
104
packages/renderer/MdToHtml/linkReplacement.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import utils from '../utils';
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
const urlUtils = require('../urlUtils.js');
|
||||
const { getClassNameForMimeType } = require('font-awesome-filetypes');
|
||||
|
||||
export interface Options {
|
||||
title?: string;
|
||||
resources?: any;
|
||||
ResourceModel?: any;
|
||||
linkRenderingType?: number;
|
||||
plainResourceRendering?: boolean;
|
||||
postMessageSyntax?: string;
|
||||
enableLongPress?: boolean;
|
||||
}
|
||||
|
||||
export default function(href: string, options: Options = null) {
|
||||
options = {
|
||||
title: '',
|
||||
resources: {},
|
||||
ResourceModel: null,
|
||||
linkRenderingType: 1,
|
||||
plainResourceRendering: false,
|
||||
postMessageSyntax: 'postMessage',
|
||||
enableLongPress: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
const resourceHrefInfo = urlUtils.parseResourceUrl(href);
|
||||
const isResourceUrl = options.resources && !!resourceHrefInfo;
|
||||
let title = options.title;
|
||||
|
||||
let resourceIdAttr = '';
|
||||
let icon = '';
|
||||
let hrefAttr = '#';
|
||||
let mime = '';
|
||||
let resourceId = '';
|
||||
if (isResourceUrl) {
|
||||
resourceId = resourceHrefInfo.itemId;
|
||||
|
||||
const result = options.resources[resourceId];
|
||||
const resourceStatus = utils.resourceStatus(options.ResourceModel, result);
|
||||
|
||||
if (result && result.item) {
|
||||
if (!title) title = result.item.title;
|
||||
mime = result.item.mime;
|
||||
}
|
||||
|
||||
if (result && resourceStatus !== 'ready' && !options.plainResourceRendering) {
|
||||
const icon = utils.resourceStatusFile(resourceStatus);
|
||||
return `<a class="not-loaded-resource resource-status-${resourceStatus}" data-resource-id="${resourceId}">` + `<img src="data:image/svg+xml;utf8,${htmlentities(icon)}"/>`;
|
||||
} else {
|
||||
href = `joplin://${resourceId}`;
|
||||
if (resourceHrefInfo.hash) href += `#${resourceHrefInfo.hash}`;
|
||||
resourceIdAttr = `data-resource-id='${resourceId}'`;
|
||||
|
||||
const iconType = mime ? getClassNameForMimeType(mime) : 'fa-joplin';
|
||||
|
||||
// Icons are defined in lib/renderers/noteStyle 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 = `<span class="resource-icon ${iconType}"></span>`;
|
||||
}
|
||||
} 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 = `${options.postMessageSyntax}(${JSON.stringify(href)}, { resourceId: ${JSON.stringify(resourceId)} }); return false;`;
|
||||
if (options.enableLongPress && !!resourceId) {
|
||||
const onClick = `${options.postMessageSyntax}(${JSON.stringify(href)})`;
|
||||
const onLongClick = `${options.postMessageSyntax}("longclick:${resourceId}")`;
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${onLongClick};}, ${utils.longPressDelay});`;
|
||||
const cancel = 'if (!!t) {clearTimeout(t); t=null;';
|
||||
const touchEnd = `${cancel} ${onClick};}`;
|
||||
js = `ontouchstart='${touchStart}' ontouchend='${touchEnd}' ontouchcancel='${cancel} ontouchmove="${cancel}'`;
|
||||
} else {
|
||||
js = `onclick='${js}'`;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
const attrHtml = [];
|
||||
attrHtml.push('data-from-md');
|
||||
if (resourceIdAttr) attrHtml.push(resourceIdAttr);
|
||||
if (title) attrHtml.push(`title='${htmlentities(title)}'`);
|
||||
if (mime) attrHtml.push(`type='${htmlentities(mime)}'`);
|
||||
|
||||
if (options.plainResourceRendering || options.linkRenderingType === 2) {
|
||||
icon = '';
|
||||
attrHtml.push(`href='${htmlentities(href)}'`);
|
||||
} else {
|
||||
attrHtml.push(`href='${hrefAttr}'`);
|
||||
if (js) attrHtml.push(js);
|
||||
}
|
||||
|
||||
return `<a ${attrHtml.join(' ')}>${icon}`;
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import { RuleOptions } from '../../MdToHtml';
|
||||
|
||||
const htmlUtils = require('../../htmlUtils.js');
|
||||
const utils = require('../../utils');
|
||||
import htmlUtils from '../../htmlUtils';
|
||||
import utils from '../../utils';
|
||||
|
||||
function renderImageHtml(before: string, src: string, after: string, ruleOptions: RuleOptions) {
|
||||
const r = utils.imageReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { RuleOptions } from '../../MdToHtml';
|
||||
|
||||
const utils = require('../../utils');
|
||||
const htmlUtils = require('../../htmlUtils.js');
|
||||
import htmlUtils from '../../htmlUtils';
|
||||
import utils from '../../utils';
|
||||
|
||||
function plugin(markdownIt: any, ruleOptions: RuleOptions) {
|
||||
const defaultRender = markdownIt.renderer.rules.image;
|
||||
@ -23,7 +22,7 @@ function plugin(markdownIt: any, ruleOptions: RuleOptions) {
|
||||
const id = r['data-resource-id'];
|
||||
|
||||
const longPressHandler = `${ruleOptions.postMessageSyntax}('longclick:${id}')`;
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${longPressHandler};}, ${ruleOptions.longPressDelay});`;
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${longPressHandler};}, ${utils.longPressDelay});`;
|
||||
const cancel = 'if (!!t) clearTimeout(t); t=null';
|
||||
|
||||
js = ` ontouchstart="${touchStart}" ontouchend="${cancel}" ontouchcancel="${cancel}" ontouchmove="${cancel}"`;
|
||||
|
@ -1,95 +1,26 @@
|
||||
import { RuleOptions } from '../../MdToHtml';
|
||||
import linkReplacement from '../linkReplacement';
|
||||
import utils from '../../utils';
|
||||
|
||||
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 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 href = utils.getAttr(token.attrs, 'href');
|
||||
const resourceHrefInfo = urlUtils.parseResourceUrl(href);
|
||||
const isResourceUrl = ruleOptions.resources && !!resourceHrefInfo;
|
||||
let title = utils.getAttr(token.attrs, 'title', isResourceUrl ? '' : href);
|
||||
const title = utils.getAttr(token.attrs, 'title', isResourceUrl ? '' : href);
|
||||
|
||||
let resourceIdAttr = '';
|
||||
let icon = '';
|
||||
let hrefAttr = '#';
|
||||
let mime = '';
|
||||
let resourceId = '';
|
||||
if (isResourceUrl) {
|
||||
resourceId = resourceHrefInfo.itemId;
|
||||
|
||||
const result = ruleOptions.resources[resourceId];
|
||||
const resourceStatus = utils.resourceStatus(ruleOptions.ResourceModel, 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 `<a class="not-loaded-resource resource-status-${resourceStatus}" data-resource-id="${resourceId}">` + `<img src="data:image/svg+xml;utf8,${htmlentities(icon)}"/>`;
|
||||
} 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 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 = `<span class="resource-icon ${iconType}"></span>`;
|
||||
}
|
||||
} 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)}, { resourceId: ${JSON.stringify(resourceId)} }); return false;`;
|
||||
if (ruleOptions.enableLongPress && !!resourceId) {
|
||||
const onClick = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)})`;
|
||||
const onLongClick = `${ruleOptions.postMessageSyntax}("longclick:${resourceId}")`;
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${onLongClick};}, ${ruleOptions.longPressDelay});`;
|
||||
const cancel = 'if (!!t) {clearTimeout(t); t=null;';
|
||||
const touchEnd = `${cancel} ${onClick};}`;
|
||||
js = `ontouchstart='${touchStart}' ontouchend='${touchEnd}' ontouchcancel='${cancel} ontouchmove="${cancel}'`;
|
||||
} else {
|
||||
js = `onclick='${js}'`;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
const attrHtml = [];
|
||||
attrHtml.push('data-from-md');
|
||||
if (resourceIdAttr) attrHtml.push(resourceIdAttr);
|
||||
if (title) attrHtml.push(`title='${htmlentities(title)}'`);
|
||||
if (mime) attrHtml.push(`type='${htmlentities(mime)}'`);
|
||||
|
||||
if (ruleOptions.plainResourceRendering || ruleOptions.linkRenderingType === 2) {
|
||||
icon = '';
|
||||
attrHtml.push(`href='${htmlentities(href)}'`);
|
||||
|
||||
// return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${htmlentities(href)}' type='${htmlentities(mime)}'>`;
|
||||
} else {
|
||||
attrHtml.push(`href='${hrefAttr}'`);
|
||||
if (js) attrHtml.push(js);
|
||||
// return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${hrefAttr}' ${js} type='${htmlentities(mime)}'>${icon}`;
|
||||
}
|
||||
|
||||
return `<a ${attrHtml.join(' ')}>${icon}`;
|
||||
return linkReplacement(href, {
|
||||
title,
|
||||
resources: ruleOptions.resources,
|
||||
ResourceModel: ruleOptions.ResourceModel,
|
||||
linkRenderingType: ruleOptions.linkRenderingType,
|
||||
plainResourceRendering: ruleOptions.plainResourceRendering,
|
||||
postMessageSyntax: ruleOptions.postMessageSyntax,
|
||||
enableLongPress: ruleOptions.enableLongPress,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { RuleOptions } from '../../MdToHtml';
|
||||
import htmlUtils from '../../htmlUtils';
|
||||
|
||||
const md5 = require('md5');
|
||||
const htmlUtils = require('../../htmlUtils');
|
||||
|
||||
export default {
|
||||
plugin: function(markdownIt: any, ruleOptions: RuleOptions) {
|
||||
|
@ -6,6 +6,8 @@ const htmlparser2 = require('@joplin/fork-htmlparser2');
|
||||
// https://stackoverflow.com/a/16119722/561309
|
||||
const imageRegex = /<img([\s\S]*?)src=["']([\s\S]*?)["']([\s\S]*?)>/gi;
|
||||
|
||||
const anchorRegex = /<a([\s\S]*?)href=["']([\s\S]*?)["']([\s\S]*?)>/gi;
|
||||
|
||||
const selfClosingElements = [
|
||||
'area',
|
||||
'base',
|
||||
@ -30,7 +32,7 @@ const selfClosingElements = [
|
||||
|
||||
class HtmlUtils {
|
||||
|
||||
attributesHtml(attr) {
|
||||
attributesHtml(attr: any) {
|
||||
const output = [];
|
||||
|
||||
for (const n in attr) {
|
||||
@ -41,10 +43,10 @@ class HtmlUtils {
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
processImageTags(html, callback) {
|
||||
processImageTags(html: string, callback: Function) {
|
||||
if (!html) return '';
|
||||
|
||||
return html.replace(imageRegex, (v, before, src, after) => {
|
||||
return html.replace(imageRegex, (_v, before, src, after) => {
|
||||
const action = callback({ src: src });
|
||||
|
||||
if (!action) return `<img${before}src="${src}"${after}>`;
|
||||
@ -66,15 +68,40 @@ class HtmlUtils {
|
||||
});
|
||||
}
|
||||
|
||||
isSelfClosingTag(tagName) {
|
||||
processAnchorTags(html: string, callback: Function) {
|
||||
if (!html) return '';
|
||||
|
||||
return html.replace(anchorRegex, (_v, before, href, after) => {
|
||||
const action = callback({ href: href });
|
||||
|
||||
if (!action) return `<a${before}href="${href}"${after}>`;
|
||||
|
||||
if (action.type === 'replaceElement') {
|
||||
return action.html;
|
||||
}
|
||||
|
||||
if (action.type === 'replaceSource') {
|
||||
return `<img${before}href="${action.href}"${after}>`;
|
||||
}
|
||||
|
||||
if (action.type === 'setAttributes') {
|
||||
const attrHtml = this.attributesHtml(action.attrs);
|
||||
return `<img${before}${attrHtml}${after}>`;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid action: ${action.type}`);
|
||||
});
|
||||
}
|
||||
|
||||
isSelfClosingTag(tagName: string) {
|
||||
return selfClosingElements.includes(tagName.toLowerCase());
|
||||
}
|
||||
|
||||
// TODO: copied from @joplin/lib
|
||||
stripHtml(html) {
|
||||
const output = [];
|
||||
stripHtml(html: string) {
|
||||
const output: string[] = [];
|
||||
|
||||
const tagStack = [];
|
||||
const tagStack: string[] = [];
|
||||
|
||||
const currentTag = () => {
|
||||
if (!tagStack.length) return '';
|
||||
@ -85,16 +112,16 @@ class HtmlUtils {
|
||||
|
||||
const parser = new htmlparser2.Parser({
|
||||
|
||||
onopentag: (name) => {
|
||||
onopentag: (name: string) => {
|
||||
tagStack.push(name.toLowerCase());
|
||||
},
|
||||
|
||||
ontext: (decodedText) => {
|
||||
ontext: (decodedText: string) => {
|
||||
if (disallowedTags.includes(currentTag())) return;
|
||||
output.push(decodedText);
|
||||
},
|
||||
|
||||
onclosetag: (name) => {
|
||||
onclosetag: (name: string) => {
|
||||
if (currentTag() === name.toLowerCase()) tagStack.pop();
|
||||
},
|
||||
|
||||
@ -106,16 +133,16 @@ class HtmlUtils {
|
||||
return output.join('').replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
sanitizeHtml(html, options = null) {
|
||||
sanitizeHtml(html: string, options: any = null) {
|
||||
options = Object.assign({}, {
|
||||
// If true, adds a "jop-noMdConv" class to all the tags.
|
||||
// It can be used afterwards to restore HTML tags in Markdown.
|
||||
addNoMdConvClass: false,
|
||||
}, options);
|
||||
|
||||
const output = [];
|
||||
const output: string[] = [];
|
||||
|
||||
const tagStack = [];
|
||||
const tagStack: string[] = [];
|
||||
|
||||
const currentTag = () => {
|
||||
if (!tagStack.length) return '';
|
||||
@ -135,7 +162,7 @@ class HtmlUtils {
|
||||
|
||||
const parser = new htmlparser2.Parser({
|
||||
|
||||
onopentag: (name, attrs) => {
|
||||
onopentag: (name: string, attrs: any) => {
|
||||
tagStack.push(name.toLowerCase());
|
||||
|
||||
if (disallowedTags.includes(currentTag())) return;
|
||||
@ -171,7 +198,7 @@ class HtmlUtils {
|
||||
output.push(`<${name}${attrHtml}${closingSign}`);
|
||||
},
|
||||
|
||||
ontext: (decodedText) => {
|
||||
ontext: (decodedText: string) => {
|
||||
if (disallowedTags.includes(currentTag())) return;
|
||||
|
||||
if (currentTag() === 'style') {
|
||||
@ -184,7 +211,7 @@ class HtmlUtils {
|
||||
}
|
||||
},
|
||||
|
||||
onclosetag: (name) => {
|
||||
onclosetag: (name: string) => {
|
||||
const current = currentTag();
|
||||
|
||||
if (current === name.toLowerCase()) tagStack.pop();
|
||||
@ -206,6 +233,4 @@ class HtmlUtils {
|
||||
|
||||
}
|
||||
|
||||
const htmlUtils = new HtmlUtils();
|
||||
|
||||
module.exports = htmlUtils;
|
||||
export default new HtmlUtils();
|
@ -1,9 +1,9 @@
|
||||
import MarkupToHtml, { MarkupLanguage } from './MarkupToHtml';
|
||||
import MdToHtml from './MdToHtml';
|
||||
const HtmlToHtml = require('./HtmlToHtml');
|
||||
import HtmlToHtml from './HtmlToHtml';
|
||||
import utils from './utils';
|
||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
||||
const assetsToHeaders = require('./assetsToHeaders');
|
||||
const utils = require('./utils');
|
||||
|
||||
export {
|
||||
MarkupToHtml,
|
||||
|
191
packages/renderer/jest.config.js
Normal file
191
packages/renderer/jest.config.js
Normal file
@ -0,0 +1,191 @@
|
||||
// For a detailed explanation regarding each configuration property, visit:
|
||||
// https://jestjs.io/docs/en/configuration.html
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/tmp/jest_rs",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
// clearMocks: false,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: undefined,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
// coverageDirectory: undefined,
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: 'v8',
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
// preset: undefined,
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: 'node',
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: [
|
||||
'**/*.test.js',
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/",
|
||||
// "\\.pnp\\.[^\\/]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
5830
packages/renderer/package-lock.json
generated
5830
packages/renderer/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,16 +12,20 @@
|
||||
"buildAssets": "node Tools/buildAssets.js",
|
||||
"prepublishOnly": "npm run buildAssets",
|
||||
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
|
||||
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json"
|
||||
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json",
|
||||
"test": "jest",
|
||||
"test-ci": "test"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.14.6",
|
||||
"jest": "^26.6.3",
|
||||
"typescript": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.8",
|
||||
"@types/jest": "^26.0.15",
|
||||
"font-awesome-filetypes": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"highlight.js": "^10.2.1",
|
||||
|
@ -9,9 +9,9 @@ const FetchStatuses = {
|
||||
FETCH_STATUS_ERROR: 3,
|
||||
};
|
||||
|
||||
const utils = {};
|
||||
const utils: any = {};
|
||||
|
||||
utils.getAttr = function(attrs, name, defaultValue = null) {
|
||||
utils.getAttr = function(attrs: string[], name: string, defaultValue: string = null) {
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
if (attrs[i][0] === name) return attrs[i].length > 1 ? attrs[i][1] : null;
|
||||
}
|
||||
@ -63,12 +63,12 @@ utils.loaderImage = function() {
|
||||
`;
|
||||
};
|
||||
|
||||
utils.resourceStatusImage = function(status) {
|
||||
utils.resourceStatusImage = function(status: string) {
|
||||
if (status === 'notDownloaded') return utils.notDownloadedResource();
|
||||
return utils.resourceStatusFile(status);
|
||||
};
|
||||
|
||||
utils.resourceStatusFile = function(status) {
|
||||
utils.resourceStatusFile = function(status: string) {
|
||||
if (status === 'notDownloaded') return utils.notDownloadedResource();
|
||||
if (status === 'downloading') return utils.loaderImage();
|
||||
if (status === 'encrypted') return utils.loaderImage();
|
||||
@ -77,7 +77,7 @@ utils.resourceStatusFile = function(status) {
|
||||
throw new Error(`Unknown status: ${status}`);
|
||||
};
|
||||
|
||||
utils.resourceStatusIndex = function(status) {
|
||||
utils.resourceStatusIndex = function(status: string) {
|
||||
if (status === 'error') return -1;
|
||||
if (status === 'notDownloaded') return 0;
|
||||
if (status === 'downloading') return 1;
|
||||
@ -87,7 +87,7 @@ utils.resourceStatusIndex = function(status) {
|
||||
throw new Error(`Unknown status: ${status}`);
|
||||
};
|
||||
|
||||
utils.resourceStatusName = function(index) {
|
||||
utils.resourceStatusName = function(index: number) {
|
||||
if (index === -1) return 'error';
|
||||
if (index === 0) return 'notDownloaded';
|
||||
if (index === 1) return 'downloading';
|
||||
@ -97,7 +97,7 @@ utils.resourceStatusName = function(index) {
|
||||
throw new Error(`Unknown index: ${index}`);
|
||||
};
|
||||
|
||||
utils.resourceStatus = function(ResourceModel, resourceInfo) {
|
||||
utils.resourceStatus = function(ResourceModel: any, resourceInfo: any) {
|
||||
if (!ResourceModel) return 'ready';
|
||||
|
||||
let resourceStatus = 'ready';
|
||||
@ -122,7 +122,7 @@ utils.resourceStatus = function(ResourceModel, resourceInfo) {
|
||||
return resourceStatus;
|
||||
};
|
||||
|
||||
utils.imageReplacement = function(ResourceModel, src, resources, resourceBaseUrl) {
|
||||
utils.imageReplacement = function(ResourceModel: any, src: string, resources: any, resourceBaseUrl: string) {
|
||||
if (!ResourceModel || !resources) return null;
|
||||
|
||||
if (!ResourceModel.isResourceUrl(src)) return null;
|
||||
@ -151,4 +151,8 @@ utils.imageReplacement = function(ResourceModel, src, resources, resourceBaseUrl
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
// Used in mobile app when enableLongPress = true. Tells for how long
|
||||
// the resource should be pressed before the menu is shown.
|
||||
utils.longPressDelay = 500;
|
||||
|
||||
export default utils;
|
Loading…
Reference in New Issue
Block a user