1 |
|
2 | (function (self, factory) {
|
3 | const plugin = factory(self)
|
4 |
|
5 | if (typeof exports === 'object' && typeof exports['nodeName'] !== 'string') {
|
6 | module.exports = plugin
|
7 | } else {
|
8 | if ('_hyperscript' in self) self._hyperscript.use(plugin)
|
9 | }
|
10 | })(typeof self !== 'undefined' ? self : this, self => {
|
11 |
|
12 | function genUUID() {
|
13 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
14 | var r = (Math.random() * 16) | 0,
|
15 | v = c == "x" ? r : (r & 0x3) | 0x8;
|
16 | return v.toString(16);
|
17 | });
|
18 | }
|
19 |
|
20 | function parseUrl(url) {
|
21 | var finalUrl = url;
|
22 | if (finalUrl.indexOf("/") === 0) {
|
23 | var basePart = window.location.hostname + (window.location.port ? ':' + window.location.port : '');
|
24 | if (window.location.protocol === 'https:') {
|
25 | finalUrl = "wss://" + basePart + finalUrl;
|
26 | } else if (window.location.protocol === 'http:') {
|
27 | finalUrl = "ws://" + basePart + finalUrl;
|
28 | }
|
29 | }
|
30 | return finalUrl;
|
31 | }
|
32 |
|
33 | function createSocket(url) {
|
34 | var parsedUrl = parseUrl(url.evaluate());
|
35 | return new WebSocket(parsedUrl);
|
36 | }
|
37 |
|
38 | |
39 |
|
40 |
|
41 | return _hyperscript => {
|
42 |
|
43 |
|
44 | var PROXY_BLACKLIST = ["then", "catch", "length", "asyncWrapper", "toJSON"];
|
45 |
|
46 | _hyperscript.addFeature("socket", function (parser, runtime, tokens) {
|
47 | function getProxy(timeout) {
|
48 | return new Proxy(
|
49 | {},
|
50 | {
|
51 | get: function (obj, property) {
|
52 | if (PROXY_BLACKLIST.indexOf(property) >= 0) {
|
53 | return null;
|
54 | } else if (property === "noTimeout") {
|
55 | return getProxy(-1);
|
56 | } else if (property === "timeout") {
|
57 | return function (i) {
|
58 | return getProxy(parseInt(i));
|
59 | };
|
60 | } else {
|
61 | return function () {
|
62 | var uuid = genUUID();
|
63 | var args = [];
|
64 | for (var i = 0; i < arguments.length; i++) {
|
65 | args.push(arguments[i]);
|
66 | }
|
67 | var rpcInfo = {
|
68 | iid: uuid,
|
69 | function: property,
|
70 | args: args,
|
71 | };
|
72 | socket = socket ? socket : createSocket(url); //recreate socket if needed
|
73 | socket.send(JSON.stringify(rpcInfo));
|
74 |
|
75 | var promise = new Promise(function (resolve, reject) {
|
76 | promises[uuid] = {
|
77 | resolve: resolve,
|
78 | reject: reject,
|
79 | };
|
80 | });
|
81 |
|
82 | if (timeout >= 0) {
|
83 | setTimeout(function () {
|
84 | if (promises[uuid]) {
|
85 | promises[uuid].reject("Timed out");
|
86 | }
|
87 | delete promises[uuid];
|
88 | }, timeout);
|
89 | }
|
90 | return promise;
|
91 | };
|
92 | }
|
93 | },
|
94 | }
|
95 | );
|
96 | }
|
97 |
|
98 | if (tokens.matchToken("socket")) {
|
99 | var name = parser.requireElement("dotOrColonPath", tokens);
|
100 | var qualifiedName = name.evaluate();
|
101 | var nameSpace = qualifiedName.split(".");
|
102 | var socketName = nameSpace.pop();
|
103 |
|
104 | var promises = {};
|
105 | var url = parser.requireElement("stringLike", tokens);
|
106 |
|
107 | var defaultTimeout = 10000;
|
108 | if (tokens.matchToken("with")) {
|
109 | tokens.requireToken("timeout");
|
110 | defaultTimeout = parser.requireElement("expression", tokens).evaluate();
|
111 | }
|
112 |
|
113 | if (tokens.matchToken("on")) {
|
114 | tokens.requireToken("message");
|
115 | if (tokens.matchToken("as")) {
|
116 | tokens.requireToken("json");
|
117 | var jsonMessages = true;
|
118 | }
|
119 | var messageHandler = parser.requireElement("commandList", tokens);
|
120 | var implicitReturn = {
|
121 | type: "implicitReturn",
|
122 | op: function (context) {
|
123 | return runtime.HALT;
|
124 | },
|
125 | execute: function (context) {
|
126 |
|
127 | },
|
128 | };
|
129 | var end = messageHandler;
|
130 | while (end.next) {
|
131 | end = end.next;
|
132 | }
|
133 | end.next = implicitReturn;
|
134 |
|
135 |
|
136 | }
|
137 |
|
138 | var socket = createSocket(url);
|
139 | var rpcProxy = getProxy(defaultTimeout);
|
140 |
|
141 | var socketObject = {
|
142 | raw: socket,
|
143 | dispatchEvent: function (evt) {
|
144 | var details = evt.detail;
|
145 |
|
146 | delete details.sender;
|
147 | delete details._namedArgList_;
|
148 | socket.send(JSON.stringify(Object.assign({ type: evt.type }, details)));
|
149 | },
|
150 | rpc: rpcProxy,
|
151 | };
|
152 |
|
153 | var socketFeature = {
|
154 | name: socketName,
|
155 | socket: socketObject,
|
156 | install: function (target) {
|
157 | runtime.assignToNamespace(target, nameSpace, socketName, socketObject);
|
158 | },
|
159 | };
|
160 |
|
161 | socket.onmessage = function (evt) {
|
162 | var data = evt.data;
|
163 | try {
|
164 | var dataAsJson = JSON.parse(data);
|
165 | } catch (e) {
|
166 |
|
167 | }
|
168 |
|
169 |
|
170 | if (dataAsJson && dataAsJson.iid) {
|
171 | if (dataAsJson.throw) {
|
172 | promises[dataAsJson.iid].reject(dataAsJson.throw);
|
173 | } else {
|
174 | promises[dataAsJson.iid].resolve(dataAsJson.return);
|
175 | }
|
176 | delete promises[dataAsJson.iid];
|
177 | }
|
178 |
|
179 | if (messageHandler) {
|
180 | var context = runtime.makeContext(socketObject, socketFeature, socketObject);
|
181 | if (jsonMessages) {
|
182 | if (dataAsJson) {
|
183 | context.message = dataAsJson;
|
184 | context.result = dataAsJson;
|
185 | } else {
|
186 | throw "Received non-JSON message from socket: " + data;
|
187 | }
|
188 | } else {
|
189 | context.message = data;
|
190 | context.result = data;
|
191 | }
|
192 | messageHandler.execute(context);
|
193 | }
|
194 | };
|
195 |
|
196 |
|
197 | socket.addEventListener("close", function (e) {
|
198 | socket = null;
|
199 | });
|
200 |
|
201 | return socketFeature;
|
202 | }
|
203 | });
|
204 | }
|
205 | })
|