mvdom
Version:
deprecated - Moved to dom-native package
111 lines (89 loc) • 3.24 kB
text/typescript
import { hub } from './hub';
// import { BaseHTMLElement } from './c-base';
// TODO: needs to be in mvdom
interface HubInfo {
topic: string,
label?: string,
ns?: string
}
type OnHubEvent = { methodName: string, hubName: string, topic: string, label?: string };
const onHubEventByConstructor: Map<Function, OnHubEvent[]> = new Map();
//#region ---------- Public onEvent Decorator ----------
/**
* `onHub` decorator to bind a hub event to this instance.
*/
export function onHub(hubName: string, topic: string, label?: string) {
// target references the element's class. It will be the constructor function for a static method or the prototype of the class for an instance member
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const clazz = target.constructor;
// get the onEvents array for this clazz
let onEvents = onHubEventByConstructor.get(clazz);
if (onEvents == null) {
onEvents = [];
onHubEventByConstructor.set(clazz, onEvents);
}
// create and push the event
const onEvent: OnHubEvent = {
methodName: propertyKey,
hubName,
topic,
label
};
onEvents.push(onEvent);
}
}
//#endregion ---------- /Public onEvent Decorator ----------
// For BaseHTMLElement
export function bindOnHubDecorators(this: any) {
let clazz = this.constructor;
const topClazz = clazz;
// keep track of the function name that were bound, to not double bind overriden parents
// This is the intuitive behavior, aligning with inheritance behavior.
const fnNameBoundSet = new Set<string>();
const opts = { ...this._nsObj, ctx: this };
while (clazz !== HTMLElement) {
const onEvents = onHubEventByConstructor.get(clazz);
if (onEvents) {
for (const onEvent of onEvents) {
const fnName = onEvent.methodName;
if (!fnNameBoundSet.has(fnName)) {
const fn = (<any>this)[fnName];
const h = hub(onEvent.hubName);
h.sub(onEvent.topic, onEvent.label, fn, opts);
// to not double bind same function name (aligning with inheritance behavior)
fnNameBoundSet.add(fnName);
}
}
}
// clazz = (<any>clazz).__proto__;
clazz = Object.getPrototypeOf(clazz);
}
}
// only unbind docEvent and winEvent
export function unbindOnHubDecorators(this: any) {
let clazz = this.constructor;
// aligning with binding logic, super methods of same name is not double bound, so, not need to unbind.
const fnNameBoundSet = new Set<string>();
// right now, we unbind by object namespace, so keep track of which hub was already unbound
const unboundHubNames = new Set<string>();
const nsObj = this._nsObj;
while (clazz !== HTMLElement) {
const onEvents = onHubEventByConstructor.get(clazz);
if (onEvents) {
for (const onEvent of onEvents) {
const hubName = onEvent.hubName;
const fnName = onEvent.methodName;
if (!fnNameBoundSet.has(fnName) && !unboundHubNames.has(hubName)) {
const h = hub(hubName);
h.unsub(nsObj);
// no need to double unbind hub
unboundHubNames.add(hubName);
// to not double bind same function name (aligning with inheritance behavior)
fnNameBoundSet.add(fnName);
}
}
}
// clazz = (<any>clazz).__proto__;
clazz = Object.getPrototypeOf(clazz);
}
}