UNPKG

6.09 kBJavaScriptView Raw
1const CurrentFunctionList = require("./current/current-function-list");
2const RemoteInterface = require("./remote-interface");
3const RemoteFunctionList = require("./remote/remote-function-list");
4const Util = require("./util/util.js");
5const GlobalEventHandler = require("./global-event-handler.js");
6const Broadcaster = require("./broadcaster.js");
7
8/**
9 * Messenger is a singleton that allows calling functions in multiple
10 * contexts
11 */
12class Messenger {
13 constructor() {
14 // generate a unique id for this instance of the messenger
15 this._id = Util.id();
16
17 // ensure the parent stack does not target itself
18 this._parentStack = RemoteInterface.default();
19
20 // allow adding local functions immedietly
21 this._currentFunctionList = new CurrentFunctionList();
22
23 // allows calling functions on everything
24 this._broadcaster = new Broadcaster(this);
25
26 // we still need to confirm if a parent exists and has the messenger
27 // framework added.. see _setup() function
28 this._parentFunctionList = undefined;
29
30 // these are the pre-registered available child objects
31 this._callableList = [];
32
33 this._setup();
34
35 return new Proxy(this, {
36 get: (target, prop, receiver) => {
37 // sets the watcher callback
38 if (prop === "onload") {
39 return (variable, callback) => {
40 if (variable === "self" || variable === "id") {
41 return callback();
42 }
43
44 if (!target[variable]) {
45 target[variable] = new RemoteFunctionList(variable);
46 }
47
48 target[variable].onload(callback);
49 };
50 }
51
52 switch (prop) {
53 case "id": return target._id;
54 case "self": return target._currentFunctionList;
55 case "broadcast": return target._broadcaster;
56 case "_setup":
57 case "_registerListeners":
58 case "_id":
59 case "_broadcaster":
60 case "_parentStack": return target[prop];
61 default:
62 break;
63 }
64
65 const targetVar = target[prop];
66
67 // return undefined if target variable doesn't exist
68 // or it has not been verified yet
69 if (!targetVar || !targetVar.isValid()) {
70 return undefined;
71 }
72
73 return target[prop];
74 }
75 });
76 }
77
78 /**
79 * Internal function call to initialise the messenger framework
80 */
81 _setup() {
82 this._registerListeners();
83
84 // if a parent exists, send a message calling for an initialisation
85 if (this._parentStack) {
86 this._parentStack.send("__messenger__child_init");
87 }
88 else {
89 console.warn("Messenger[" + this._id + "] does not have a parent. Plattar.messenger.parent will be undefined");
90 }
91 }
92
93 /**
94 * Register all critical listener interfaces so the framework can function correctly
95 */
96 _registerListeners() {
97 GlobalEventHandler.instance().listen("__messenger__child_init", (src, data) => {
98 const iframeID = src.id;
99
100 // check reserved key list
101 switch (iframeID) {
102 case undefined: throw new Error("Messenger[" + this._id + "].setup() Component ID cannot be undefined");
103 case "self": throw new Error("Messenger[" + this._id + "].setup() Component ID of \"self\" cannot be used as the keyword is reserved");
104 case "parent": throw new Error("Messenger[" + this._id + "].setup() Component ID of \"parent\" cannot be used as the keyword is reserved");
105 case "id": throw new Error("Messenger[" + this._id + "].setup() Component ID of \"id\" cannot be used as the keyword is reserved");
106 case "onload": throw new Error("Messenger[" + this._id + "].setup() Component ID of \"onload\" cannot be used as the keyword is reserved");
107 default:
108 break;
109 }
110
111 // initialise the child iframe as a messenger pipe
112 if (!this[iframeID]) {
113 this[iframeID] = new RemoteFunctionList(iframeID);
114 }
115
116 this[iframeID].setup(new RemoteInterface(src.source, src.origin));
117
118 // add the interface to the broadcaster
119 this._broadcaster._push(iframeID);
120
121 src.send("__messenger__parent_init");
122 });
123
124 GlobalEventHandler.instance().listen("__messenger__parent_init", (src, data) => {
125 if (!this["parent"]) {
126 this["parent"] = new RemoteFunctionList("parent");
127 }
128
129 this["parent"].setup(new RemoteInterface(src.source, src.origin));
130 });
131
132 // this listener will fire remotely to execute a function in the current
133 // context
134 GlobalEventHandler.instance().listen("__messenger__exec_fnc", (src, data) => {
135 const instanceID = data.instance_id;
136 const args = data.function_args;
137 const fname = data.function_name;
138
139 // using JS reflection, execute the local function
140 Plattar.messenger.self[fname](...args).then((res) => {
141 src.send("__messenger__exec_fnc_result", {
142 function_status: "success",
143 function_name: fname,
144 function_args: res,
145 instance_id: instanceID
146 });
147 }).catch((err) => {
148 src.send("__messenger__exec_fnc_result", {
149 function_status: "error",
150 function_name: fname,
151 function_args: err.message,
152 instance_id: instanceID
153 });
154 });
155 });
156 }
157}
158
159module.exports = Messenger;
\No newline at end of file