graph-explorer
Version:
Graph Explorer can be used to explore and RDF graphs in SPARQL endpoints or on the web.
149 lines (132 loc) • 3.63 kB
text/typescript
export type Listener<Data, Key extends keyof Data> = (
data: Data[Key],
key: Key
) => void;
export type AnyListener<Data> = (data: Partial<Data>, key: string) => void;
export type Unsubscribe = () => void;
export interface PropertyChange<Source, Value> {
source: Source;
previous: Value;
}
export interface AnyEvent<Data> {
key: string;
data: Partial<Data>;
}
export interface Events<Data> {
on<Key extends keyof Data>(
eventKey: Key,
listener: Listener<Data, Key>
): void;
off<Key extends keyof Data>(
eventKey: Key,
listener: Listener<Data, Key>
): void;
onAny(listener: AnyListener<Data>): void;
offAny(listener: AnyListener<Data>): void;
}
export class EventSource<Data> implements Events<Data> {
private listeners = new Map<keyof Data, Listener<Data, any>[]>();
private anyListeners: AnyListener<Data>[] | undefined;
on<Key extends keyof Data>(
eventKey: Key,
listener: Listener<Data, Key>
): void {
let listeners = this.listeners.get(eventKey);
if (!listeners) {
listeners = [];
this.listeners.set(eventKey, listeners);
}
listeners.push(listener);
}
onAny(listener: AnyListener<Data>): void {
let listeners = this.anyListeners;
if (!listeners) {
listeners = [];
this.anyListeners = listeners;
}
listeners.push(listener);
}
off<Key extends keyof Data>(
eventKey: Key,
listener: Listener<Data, Key>
): void {
const listeners = this.listeners.get(eventKey);
if (!listeners) {
return;
}
const index = listeners.indexOf(listener);
if (index >= 0) {
listeners.splice(index, 1);
}
}
offAny(listener: AnyListener<Data>): void {
const listeners = this.anyListeners;
if (!listeners) {
return;
}
const index = listeners.indexOf(listener);
if (index >= 0) {
listeners.splice(index, 1);
}
}
trigger<Key extends keyof Data>(eventKey: Key, data: Data[Key]): void {
const listeners = this.listeners.get(eventKey);
if (listeners) {
for (const listener of listeners) {
listener(data, eventKey);
}
}
if (this.anyListeners) {
for (const anyListener of this.anyListeners) {
anyListener({ [eventKey]: data } as any, eventKey as string);
}
}
}
}
export class EventObserver {
private unsubscribeByKey = new Map<string, Unsubscribe[]>();
private onDispose: Unsubscribe[] = [];
listen<Data, Key extends keyof Data>(
events: Events<Data>,
eventKey: Key,
listener: Listener<Data, Key>
) {
events.on(eventKey, listener);
this.onDispose.push(() => events.off(eventKey, listener));
}
listenAny<Data>(events: Events<Data>, listener: AnyListener<Data>) {
events.onAny(listener);
this.onDispose.push(() => events.offAny(listener));
}
listenOnce<Data, Key extends keyof Data>(
events: Events<Data>,
eventKey: Key,
listener: Listener<Data, Key>
) {
let handled = false;
const onceListener: Listener<Data, Key> = (data, key) => {
handled = true;
events.off(eventKey, onceListener);
listener(data, key);
};
events.on(eventKey, onceListener);
this.onDispose.push(() => {
if (handled) {
return;
}
events.off(eventKey, onceListener);
});
}
stopListening() {
for (const unsubscribe of this.onDispose) {
unsubscribe();
}
this.onDispose.length = 0;
this.unsubscribeByKey.forEach((unsubscribers) => {
for (const unsubscribe of unsubscribers) {
unsubscribe();
}
});
this.unsubscribeByKey.clear();
}
}