altair-graphql-core
Version:
Several of the core logic for altair graphql client
125 lines • 4.73 kB
JavaScript
import { PLUGIN_CREATE_ACTION_EVENT, PLUGIN_ENGINE_READY, PLUGIN_GET_APP_STYLE_URL_EVENT, PLUGIN_SUBSCRIBE_TO_EVENT, getActionEvent, } from './events';
import { instanceTypes } from './frame-worker';
const mainInstanceOnlyEvents = [
'createPanel',
'destroyPanel',
'createAction',
];
// methods to be excluded from the generic listener creation since they are handled specially
const speciallyHandledMethods = ['on', 'createAction'];
function getCssStyles(relevantClasses) {
try {
const styleSheets = Array.from(document.styleSheets);
return styleSheets
.map((sheet) => {
return Array.from(sheet.cssRules)
.map((rule) => rule.cssText)
.join('');
})
.filter((css) => {
return relevantClasses.some((htmlClass) => css.includes(`.${htmlClass}`));
});
}
catch {
return [];
}
}
export class PluginParentEngine {
constructor(worker, opts) {
this.worker = worker;
this.opts = opts;
this.subscribedEvents = [];
}
start(context) {
this.context = context;
this.prepareListeners();
this.handleEvents();
if (this.worker.getInstanceType() === instanceTypes.MAIN) {
this.handleActionEvents();
}
// send a message to the worker to indicate that the plugin engine is ready
this.worker.send(PLUGIN_ENGINE_READY);
}
prepareListeners() {
if (!this.context) {
return;
}
// loop over all the context object method and create a listener for each
Object.entries(this.context).forEach(([k, fn]) => {
// skip the methods that are handled specially
if (speciallyHandledMethods.includes(k)) {
return;
}
// skip the main frame only events if the frame is not the main frame
if (this.worker.getInstanceType() !== instanceTypes.MAIN &&
mainInstanceOnlyEvents.includes(k)) {
return;
}
this.worker.respond(k, async (...args) => {
return fn(...args);
});
});
}
handleEvents() {
this.worker.respond(PLUGIN_SUBSCRIBE_TO_EVENT, async (event) => {
if (typeof event !== 'string') {
return;
}
// we only need to subscribe to an event once here. In the frame, we manage the listeners separately
if (this.subscribedEvents.includes(event)) {
return;
}
if (!this.context) {
return;
}
this.context.on(event, (...args) => {
this.worker.request(event, ...args);
});
// keep track of the events that have been subscribed to
this.subscribedEvents.push(event);
});
this.worker.respond(PLUGIN_GET_APP_STYLE_URL_EVENT, async () => {
const styleSheets = Array.from(document.styleSheets);
// Get the style sheet URLs
// FYI for some reason I haven't figured out yet, we can't link to the stylesheets
// in the browser extensions directly from the sandboxed iframe.
const styleUrls = styleSheets
.map((sheet) => {
if (sheet?.href) {
return sheet.href;
}
return '';
})
.filter(Boolean);
const htmlClasses = Array.from(document.documentElement.classList);
// Get the styles that are applicable to the current theme of the page
// Doesn't work crossorigin cases. e.g. when loading from CDN. Fallback to theme instead.
const styles = getCssStyles(htmlClasses).filter((s) => s.trim().length > 0);
return { styleUrls, styles, htmlClasses, theme: this.opts?.theme };
});
}
handleActionEvents() {
// handle the createAction method specially
this.worker.respond(PLUGIN_CREATE_ACTION_EVENT, async (opts) => {
if (!this.context) {
return;
}
let id = '';
// create action and assign the real id to the id variable
id = await this.context.createAction({
...opts,
execute: async (...args) => {
if (!id) {
return;
}
return this.worker.request(getActionEvent(id), ...args);
},
});
return id;
});
}
destroy() {
this.worker.destroy();
}
}
//# sourceMappingURL=parent-engine.js.map