diff options
-rw-r--r-- | static/event-hub.ts | 112 | ||||
-rw-r--r-- | static/hub.js | 59 |
2 files changed, 114 insertions, 57 deletions
diff --git a/static/event-hub.ts b/static/event-hub.ts new file mode 100644 index 000000000..3ecaefc41 --- /dev/null +++ b/static/event-hub.ts @@ -0,0 +1,112 @@ +// Copyright (c) 2022, Compiler Explorer Authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import GoldenLayout from 'golden-layout'; +import Sentry from '@sentry/browser'; + +export type EventHubCallback<T extends unknown[]> = (...args: T) => void; + +export interface DependencyProxies<T1 extends unknown[], T2 extends unknown[] = T1> { + dependencyProxy: EventHubCallback<T1>; + dependentProxy: EventHubCallback<T2>; +} + +export interface Event<T extends unknown[], C = any> { + evt: string; + fn: EventHubCallback<T>; + ctx: C; +} + +/** + * Event Hub which is enclosed inside a Hub, allowing for dependent calls and + * deferred execution based on the parent Hub. + */ +export class EventHub { + private readonly hub: any; /* typeof Hub */ + private readonly layoutEventHub: GoldenLayout.EventEmitter; + private subscriptions: Event<any>[] = []; + + public constructor(hub: any, layoutEventHub: GoldenLayout.EventEmitter) { + this.hub = hub; + this.layoutEventHub = layoutEventHub; + } + + /** + * Emit an event to the layout event hub. + * + * Events are deferred by the parent hub during initialization to allow all + * components to install their listeners before the emission is performed. + * This fixes some ordering issues. + */ + public emit(event: string, ...args: unknown[]): void { + if (this.hub.deferred) { + this.hub.deferredEmissions.push(event, args); + } else { + this.layoutEventHub.emit(event, ...args); + } + } + + /** Attach a listener to the layout event hub. */ + public on<T extends unknown[], C = any>(event: string, callback: EventHubCallback<T>, context: C): void { + this.layoutEventHub.on(event, callback, context); + this.subscriptions.push({ evt: event, fn: callback, ctx: context }); + } + + /** Remove all listeners from the layout event hub. */ + public unsubscribe(): void { + for (const subscription of this.subscriptions) { + try { + this.layoutEventHub.off(subscription.evt, subscription.fn, subscription.ctx); + } catch (e) { + Sentry.captureMessage(`Can not unsubscribe from ${subscription.evt.toString()}`); + Sentry.captureException(e); + } + } + this.subscriptions = []; + } + + public mediateDependentCalls<T1 extends unknown[], T2 extends unknown[]= T1>( + dependent: EventHubCallback<any>, + dependency: EventHubCallback<any> + ): DependencyProxies<T1, T2> { + let hasDependencyExecuted = false; + let lastDependentArguments: unknown[] | null = null; + const dependencyProxy = function (this: unknown, ...args: unknown[]) { + dependency.apply(this, args); + hasDependencyExecuted = true; + if (lastDependentArguments) { + dependent.apply(this, lastDependentArguments); + lastDependentArguments = null; + } + }; + const dependentProxy = function (this: unknown, ...args: unknown[]) { + if (hasDependencyExecuted) { + dependent.apply(this, args); + } else { + lastDependentArguments = args; + } + }; + return { dependencyProxy, dependentProxy }; + } +} diff --git a/static/hub.js b/static/hub.js index f268ed7b2..122858571 100644 --- a/static/hub.js +++ b/static/hub.js @@ -25,7 +25,6 @@ 'use strict'; var _ = require('underscore'); -var Sentry = require('@sentry/browser'); var editor = require('./panes/editor'); var compiler = require('./panes/compiler'); var tree = require('./panes/tree'); @@ -51,6 +50,7 @@ var cfgView = require('./panes/cfg-view'); var conformanceView = require('./panes/conformance-view'); var CompilerService = require('compiler-service').CompilerService; var IdentifierSet = require('./identifier-set').IdentifierSet; +var EventHub = require('./event-hub').EventHub; function Hub(layout, subLangId, defaultLangId) { this.layout = layout; @@ -353,63 +353,8 @@ Hub.prototype.confomanceFactory = function (container, state) { return new conformanceView.Conformance(this, container, state); }; -function WrappedEventHub(hub, eventHub) { - this.hub = hub; - this.eventHub = eventHub; - this.subscriptions = []; -} - -WrappedEventHub.prototype.emit = function () { - // Events are deferred during initialisation to allow all the components to install their listeners before - // all the emits are done. This fixes some ordering issues. - if (this.hub.deferred) { - this.hub.deferredEmissions.push(arguments); - } else { - this.eventHub.emit.apply(this.eventHub, arguments); - } -}; - -WrappedEventHub.prototype.on = function (event, callback, context) { - this.eventHub.on(event, callback, context); - this.subscriptions.push({evt: event, fn: callback, ctx: context}); -}; - -WrappedEventHub.prototype.unsubscribe = function () { - _.each(this.subscriptions, _.bind(function (obj) { - try { - this.eventHub.off(obj.evt, obj.fn, obj.ctx); - } catch (e) { - Sentry.captureMessage('Can not unsubscribe from ' + obj.evt.toString()); - Sentry.captureException(e); - } - }, this)); - this.subscriptions = []; -}; - -WrappedEventHub.prototype.mediateDependentCalls = function (dependent, dependency) { - var dependencyExecuted = false; - var lastDependentArgs = null; - var dependencyProxy = function () { - dependency.apply(this, arguments); - dependencyExecuted = true; - if (lastDependentArgs) { - dependent.apply(this, lastDependentArgs); - lastDependentArgs = null; - } - }; - var dependentProxy = function () { - if (dependencyExecuted) { - dependent.apply(this, arguments); - } else { - lastDependentArgs = arguments; - } - }; - return {dependencyProxy: dependencyProxy, - dependentProxy: dependentProxy}; -}; - Hub.prototype.createEventHub = function () { - return new WrappedEventHub(this, this.layout.eventHub); + return new EventHub(this, this.layout.eventHub); }; Hub.prototype.findParentRowOrColumn = function (elem) { |