124 lines
3.6 KiB
TypeScript
Raw Normal View History

2018-03-25 21:29:35 -04:00
import ScriptContext from "./script_context.js";
import server from "./server.js";
2019-10-20 10:00:18 +02:00
import toastService from "./toast.js";
2021-04-16 23:01:56 +02:00
import froca from "./froca.js";
import utils from "./utils.js";
import { t } from "./i18n.js";
2024-12-21 16:43:50 +02:00
import { Entity } from "./frontend_script_api.js";
2024-12-21 16:43:50 +02:00
// TODO: Deduplicate with server.
interface Bundle {
script: string;
noteId: string;
allNoteIds: string[];
}
interface Widget {
parentWidget?: string;
}
async function getAndExecuteBundle(noteId: string, originEntity = null, script = null, params = null) {
const bundle = await server.post<Bundle>(`script/bundle/${noteId}`, {
script,
params
});
2019-08-17 11:28:36 +02:00
return await executeBundle(bundle, originEntity);
}
2024-12-21 16:43:50 +02:00
async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery<HTMLElement>) {
2020-02-25 19:19:10 +01:00
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container);
try {
return await (function () {
return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`);
}.call(apiContext));
2024-12-21 16:43:50 +02:00
} catch (e: any) {
2021-04-16 22:57:37 +02:00
const note = await froca.getNote(bundle.noteId);
2020-08-15 00:06:26 +02:00
2024-12-21 16:43:50 +02:00
toastService.showAndLogError(`Execution of JS note "${note?.title}" with ID ${bundle.noteId} failed with error: ${e?.message}`);
}
}
async function executeStartupBundles() {
const isMobile = utils.isMobile();
2024-12-21 16:43:50 +02:00
const scriptBundles = await server.get<Bundle[]>("script/startup" + (isMobile ? "?mobile=true" : ""));
for (const bundle of scriptBundles) {
await executeBundle(bundle);
}
}
2020-03-16 23:25:52 +01:00
class WidgetsByParent {
2024-12-21 16:43:50 +02:00
private byParent: Record<string, Widget[]>;
2020-03-16 23:25:52 +01:00
constructor() {
this.byParent = {};
}
2024-12-21 16:43:50 +02:00
add(widget: Widget) {
2020-03-16 23:25:52 +01:00
if (!widget.parentWidget) {
console.log(`Custom widget does not have mandatory 'parentWidget' property defined`);
2020-03-16 23:25:52 +01:00
return;
}
this.byParent[widget.parentWidget] = this.byParent[widget.parentWidget] || [];
this.byParent[widget.parentWidget].push(widget);
}
2024-12-21 16:43:50 +02:00
get(parentName: string) {
if (!this.byParent[parentName]) {
return [];
}
return this.byParent[parentName]
// previously, custom widgets were provided as a single instance, but that has the disadvantage
// for splits where we actually need multiple instaces and thus having a class to instantiate is better
// https://github.com/zadam/trilium/issues/4274
2024-12-21 16:43:50 +02:00
.map((w: any) => w.prototype ? new w() : w);
2020-03-16 23:25:52 +01:00
}
}
2020-03-16 21:16:09 +01:00
async function getWidgetBundlesByParent() {
2024-12-21 16:43:50 +02:00
const scriptBundles = await server.get<Bundle[]>("script/widgets");
2020-03-16 21:16:09 +01:00
2020-03-16 23:25:52 +01:00
const widgetsByParent = new WidgetsByParent();
2020-03-16 21:16:09 +01:00
for (const bundle of scriptBundles) {
let widget;
try {
widget = await executeBundle(bundle);
if (widget) {
widget._noteId = bundle.noteId;
widgetsByParent.add(widget);
}
2024-12-21 16:43:50 +02:00
} catch (e: any) {
const noteId = bundle.noteId;
const note = await froca.getNote(noteId);
toastService.showPersistent({
title: t("toast.bundle-error.title"),
icon: "alert",
message: t("toast.bundle-error.message", {
id: noteId,
2024-12-21 16:43:50 +02:00
title: note?.title,
message: e.message
})
});
logError("Widget initialization failed: ", e);
2020-03-16 21:16:09 +01:00
continue;
}
}
2020-03-16 23:25:52 +01:00
return widgetsByParent;
2020-03-16 21:16:09 +01:00
}
export default {
executeBundle,
getAndExecuteBundle,
2020-03-16 21:16:09 +01:00
executeStartupBundles,
getWidgetBundlesByParent
2020-06-14 14:30:57 +02:00
}