-
Notifications
You must be signed in to change notification settings - Fork 4
/
postmessagerpc.js
109 lines (88 loc) · 2.62 KB
/
postmessagerpc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
Example usage:
var rpc = new PostMessageRPC(window.postMessage, "thispage");
window.addEventListener("message", rpc.onMessage);
rpc.serve("getThingy", function () {
return Promise.resolve("Hello World!");
});
rpc.call("getThingy").then(function (resp) {
console.log(resp); // "Hello World!"
});
A window.postMessage() on the client side needs to be connected to the
server's onmessage event - and the other way around for this to work.
Also, the thisName in the constructor needs to match with the one passed
to PostMessageRPC.call.
When the connection isn't sound, the promise PostMessageRPC.call returns
is never resolved.
*/
"use strict";
var Promise = Promise || require("lie");
function PostMessageRPC(postMessage, thisName) {
this._postMessage = function (msg) {
postMessage(msg, "*");
};
this._thisName = thisName;
this.onMessage = this._onMessage.bind(this);
this._idCount = 0;
this._served = {};
this._responseHandlers = {};
}
module.exports = PostMessageRPC;
PostMessageRPC.prototype._onMessage = function (event) {
// || event for not-so-strict implementations (i.e. in browser addons)
var info = event.data || event;
if (info.destination === this._thisName) {
this._handleServing(info);
this._handleResponses(info);
}
};
PostMessageRPC.prototype._handleServing = function (info) {
var self = this;
if (info.type !== "call") {
return;
}
var msg = {};
Promise.resolve().then(function () {
return self._served[info.name].apply(null, info.arguments);
}).then(function (resp) {
msg.response = resp;
}).catch(function (err) {
msg.error = err;
}).then(function () {
msg.id = info.id;
msg.type = "response";
msg.destination = info.returnto;
self._postMessage(msg);
});
};
PostMessageRPC.prototype._handleResponses = function (info) {
if (info.type === "response" && this._responseHandlers[info.id]) {
this._responseHandlers[info.id](info);
}
};
PostMessageRPC.prototype.serve = function (name, func) {
this._served[name] = func;
};
PostMessageRPC.prototype.call = function (destination, name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 2);
var currentId = self._idCount++;
return new Promise(function (resolve, reject) {
self._responseHandlers[currentId] = function (data) {
delete self._responseHandlers[currentId];
if (data.error) {
reject(data.error);
} else {
resolve(data.response);
}
};
self._postMessage({
id: currentId,
name: name,
arguments: args,
type: "call",
destination: destination,
returnto: self._thisName
});
});
};