36b959a3fd42ee0a88b18bb310a22827ec3c61fc
[sdc/sdc-pubsub.git] / lib / base-pubsub.ts
1 declare const window: Window;
2
3 export class BasePubSub {
4
5     subscribers: Map<string, ISubscriber>;
6     eventsCallbacks: Function[];
7     clientId: string;
8     eventsToWait: Map<string, string[]>;
9     lastEventNotified: string;
10
11     constructor(pluginId: string) {
12         this.subscribers = new Map<string, ISubscriber>();
13         this.eventsCallbacks = [];
14         this.eventsToWait = new Map<string, string[]>();
15         this.clientId = pluginId;
16         this.lastEventNotified = '';
17         this.onMessage = this.onMessage.bind(this);
18
19         window.addEventListener('message', this.onMessage);
20     }
21
22     public register(subscriberId: string, subscriberWindow: Window, subscriberUrl: string) {
23         const subscriber = {
24             window: subscriberWindow,
25             locationUrl: subscriberUrl || subscriberWindow.location.href
26         } as ISubscriber;
27
28         this.subscribers.set(subscriberId, subscriber);
29     }
30
31     public unregister(subscriberId: string) {
32         this.subscribers.delete(subscriberId);
33     }
34
35     public on(callback: Function) {
36         const functionExists = this.eventsCallbacks.find((func: Function) => {
37             return callback.toString() === func.toString();
38         });
39
40         if (!functionExists) {
41             this.eventsCallbacks.push(callback);
42         }
43     }
44
45     public off(callback: Function) {
46         const index = this.eventsCallbacks.indexOf(callback);
47         this.eventsCallbacks.splice(index, 1);
48     }
49
50     public notify(eventType: string, eventData?: any) {
51         const eventObj = {
52             type: eventType,
53             data: eventData,
54             originId: this.clientId
55         } as IPubSubEvent;
56
57         this.subscribers.forEach( (subscriber: ISubscriber, subscriberId: string) => {
58             subscriber.window.postMessage(eventObj, subscriber.locationUrl);
59         });
60
61         this.lastEventNotified = eventType;
62
63         return {
64             subscribe: function(callbackFn) {
65
66                 if (this.subscribers.size !== 0) {
67                     const subscribersToNotify = Array.from(this.subscribers.keys());
68
69                     const checkNotifyComplete = (subscriberId: string) => {
70
71                         const index = subscribersToNotify.indexOf(subscriberId);
72                         subscribersToNotify.splice(index, 1);
73
74                         if (subscribersToNotify.length === 0) {
75                             callbackFn();
76                         }
77                     };
78
79                     this.subscribers.forEach((subscriber: ISubscriber, subscriberId: string) => {
80                         if (this.eventsToWait.has(subscriberId) &&
81                             this.eventsToWait.get(subscriberId).indexOf(eventType) !== -1) {
82
83                             const actionCompletedFunction = (actionCompletedEventData, subId = subscriberId) => {
84                                 if (actionCompletedEventData.type === 'ACTION_COMPLETED') {
85                                     checkNotifyComplete(subId);
86                                 }
87                                 this.off(actionCompletedFunction);
88
89                             };
90                             this.on(actionCompletedFunction);
91                         } else {
92                             checkNotifyComplete(subscriberId);
93                         }
94                     });
95                 } else {
96                     callbackFn();
97                 }
98             }.bind(this)
99         };
100     }
101
102     public isWaitingForEvent(eventName: string): boolean {
103         return Array.from(this.eventsToWait.values()).some((eventsList: string[]) =>
104             eventsList.indexOf(eventName) !== -1
105         );
106     }
107
108     protected onMessage(event: any) {
109         if (this.subscribers.has(event.data.originId)) {
110             this.eventsCallbacks.forEach((callback: Function) => {
111                 callback(event.data, event);
112             });
113         }
114     }
115 }
116
117 export interface IPubSubEvent {
118     type: string;
119     originId: string;
120     data: any;
121 }
122
123 export interface ISubscriber {
124     window: Window;
125     locationUrl: string;
126 }