@naanlang/naan 1.7.0 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,7 +36,7 @@
36
36
  *
37
37
  */
38
38
 
39
- var CurrentCacheName = "Naanlang-1.7.0+1";
39
+ var CurrentCacheName = "Naanlang-1.7.2+1";
40
40
 
41
41
 
42
42
  //
@@ -92,18 +92,18 @@ var terminated; // flag that this service worker
92
92
  var pubVersion = event.data.hereIsMyVersion;
93
93
  var sourceId = event.source.id; // client id of sender of message
94
94
  msgPorts[sourceId] = msgport;
95
- console.log("[1.7.0+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
- if (pubVersion != "1.7.0+1") {
95
+ console.log("[1.7.2+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
+ if (pubVersion != "1.7.2+1") {
97
97
  if (upgradeMsgPorts) { // if not activated
98
- console.log("[1.7.0+1] update pending for", sourceId);
98
+ console.log("[1.7.2+1] update pending for", sourceId);
99
99
  upgradeMsgPorts[sourceId] = msgport;
100
100
  }
101
101
  else {
102
102
  msgport.postMessage({ // notify new version available
103
103
  id: "upgrade",
104
- version: "1.7.0+1"
104
+ version: "1.7.2+1"
105
105
  });
106
- console.log("[1.7.0+1] update sent for:", sourceId);
106
+ console.log("[1.7.2+1] update sent for:", sourceId);
107
107
  }
108
108
  }
109
109
  Reaper(); // clean up obsolete info
@@ -134,10 +134,10 @@ var terminated; // flag that this service worker
134
134
  msgport.onmessage = function(msg) {
135
135
  msg = msg.data;
136
136
  if (msg.id == "skipWaiting") { // try to activate this SW now
137
- console.log("[1.7.0+1] attempting skipWaiting");
137
+ console.log("[1.7.2+1] attempting skipWaiting");
138
138
  self.skipWaiting();
139
139
  } else if (msg.id == "terminate") { // termiante this SW now
140
- console.log("[1.7.0+1] terminated");
140
+ console.log("[1.7.2+1] terminated");
141
141
  terminate();
142
142
  } else if (msg.id == "abortURL") { // abort an I/O transaction
143
143
  var href = new URL(msg.url).href;
@@ -150,7 +150,7 @@ var terminated; // flag that this service worker
150
150
  } else if (msg.id == "response") // response from fetch request
151
151
  processResponse(msg);
152
152
  else if (msg.id == "text") // just log some text
153
- console.log("[1.7.0+1] msg received:", msg.text);
153
+ console.log("[1.7.2+1] msg received:", msg.text);
154
154
  };
155
155
 
156
156
  // send text to IDE log
@@ -161,7 +161,7 @@ var terminated; // flag that this service worker
161
161
  // don't clutter the log
162
162
  msgport.postMessage({
163
163
  id: "text",
164
- text: "port received by 1.7.0+1",
164
+ text: "port received by 1.7.2+1",
165
165
  });
166
166
  */
167
167
  if (--pending === 0)
@@ -178,7 +178,7 @@ var terminated; // flag that this service worker
178
178
  includeUncontrolled: true
179
179
  }).then(function(clientList) {
180
180
  clientList.every(function(client) {
181
- console.log("[1.7.0+1] requesting new msgport for", client.id);
181
+ console.log("[1.7.2+1] requesting new msgport for", client.id);
182
182
  ++pending;
183
183
  client.postMessage({ // tell client(s) we need this fetch source
184
184
  msg: "Naan_need_fetch_port",
@@ -224,12 +224,12 @@ function Reaper() {
224
224
  clients[clientList[clidex].id] = clientList[clidex];
225
225
  for (var sourceId in msgPorts)
226
226
  if (!clients[sourceId]) {
227
- console.log("[1.7.0+1] source gone:", sourceId);
227
+ console.log("[1.7.2+1] source gone:", sourceId);
228
228
  delete msgPorts[sourceId]; // no longer a source
229
229
  }
230
230
  for (var clientId in fetchPorts)
231
231
  if (!clients[clientId]) {
232
- console.log("[1.7.0+1] client gone:", clientId);
232
+ console.log("[1.7.2+1] client gone:", clientId);
233
233
  delete fetchPorts[clientId]; // no longer a client
234
234
  }
235
235
  for (var fqdex = 0; fqdex < fetchQueue.length; ++fqdex) {
@@ -263,16 +263,16 @@ function ClearCaches() {
263
263
  return (Promise.all(
264
264
  cacheNames.map(function(cacheName) {
265
265
  if (cacheName != CurrentCacheName) {
266
- console.log('[1.7.0+1] deleting old cache:', cacheName);
266
+ console.log('[1.7.2+1] deleting old cache:', cacheName);
267
267
  return (caches.delete(cacheName));
268
268
  }
269
269
  })
270
270
  ));
271
271
  }).then(function() { // claim all clients
272
- console.log('[1.7.0+1] claiming clients');
272
+ console.log('[1.7.2+1] claiming clients');
273
273
  return (self.clients.claim());
274
274
  }).then(function() {
275
- console.log('[1.7.0+1] clients claimed');
275
+ console.log('[1.7.2+1] clients claimed');
276
276
  return (Promise.resolve(true));
277
277
  });
278
278
  return (promise);
@@ -315,7 +315,7 @@ function GetClientResponse(event, urlpath) {
315
315
  msgport.postMessage({
316
316
  id: "fetch",
317
317
  seq: seqno,
318
- version: "1.7.0+1",
318
+ version: "1.7.2+1",
319
319
  request: {
320
320
  method: event.request.method,
321
321
  url: event.request.url
@@ -363,7 +363,7 @@ function GetClientResponse(event, urlpath) {
363
363
  */
364
364
 
365
365
  self.addEventListener('install', function(event) {
366
- console.log("[1.7.0+1] install");
366
+ console.log("[1.7.2+1] install");
367
367
  self.skipWaiting();
368
368
  });
369
369
 
@@ -376,20 +376,20 @@ self.addEventListener('install', function(event) {
376
376
  */
377
377
 
378
378
  self.addEventListener('activate', function(event) {
379
- console.log("[1.7.0+1] activate");
379
+ console.log("[1.7.2+1] activate");
380
380
  self.clients.matchAll({ // for debugging, list controlled clients
381
381
  includeUncontrolled: true
382
382
  }).then(function(clientList) {
383
383
  var urls = clientList.map(function(client) {
384
384
  return (client.url);
385
385
  });
386
- console.log('[1.7.0+1] matching clients:', urls.join(', '));
386
+ console.log('[1.7.2+1] matching clients:', urls.join(', '));
387
387
  });
388
388
  var promise = ClearCaches().then(function() {
389
389
  for (var sourceId in upgradeMsgPorts) {
390
390
  upgradeMsgPorts[sourceId].postMessage({ // notify new version available
391
391
  id: "upgrade",
392
- version: "1.7.0+1"
392
+ version: "1.7.2+1"
393
393
  });
394
394
  }
395
395
  upgradeMsgPorts = false;
@@ -421,7 +421,7 @@ self.addEventListener('fetch', function(event) {
421
421
  var promise;
422
422
  var url = new URL(request.url);
423
423
  var nocache = request.method != "GET"
424
- || url.search.length !== 0 && url.searchParams.get("naanver") !== "a46b70b86094c7f89227b899497a7b9a"
424
+ || url.search.length !== 0 && url.searchParams.get("naanver") !== "3bf8aca91851d612b349539bfba24f9c"
425
425
  || request.headers.get('range');
426
426
  if (url.pathname.startsWith("/run/")) {
427
427
  nocache = true;
@@ -455,7 +455,7 @@ self.addEventListener('fetch', function(event) {
455
455
  statusText: "Request Cancelled"
456
456
  }));
457
457
  }
458
- console.log("[1.7.0+1] fetch failed", request.url, e);
458
+ console.log("[1.7.2+1] fetch failed", request.url, e);
459
459
  return (new Response(undefined, {
460
460
  status: 404,
461
461
  statusText: "Fetch Failed"
@@ -132,10 +132,12 @@ closure TermHost(track, name, options,
132
132
  *
133
133
  * Options:
134
134
  * {
135
- * workerID: <string> // kind of worker in server subsystem
135
+ * workerID: <string> // kind of worker in server subsystem
136
136
  * // "NaanIDE" for local IDE
137
- * startup: <dictionary> // startup options
138
- * type: <string> // (rare) target category, e.g. "Local"
137
+ * startup: <dictionary> // startup options
138
+ * type: <string> // (rare) target category, e.g. "Local"
139
+ * worker: <xobject> // existing worker or window
140
+ * naancont: <xobject> // NaaN controller if vsite
139
141
  * }
140
142
  *
141
143
  */
@@ -153,11 +155,17 @@ closure TermLocal(track, name, options, local target, worker, wowbot, error)
153
155
  return (termIDE(track, name, options.workerID, js.t)) // connect to IDE execution instance itself
154
156
  }
155
157
 
156
- target = findIDEtarget(track, name, options.workerID)
157
- if target
158
- return (list(false, target)) // already exists
158
+ target = findIDEtarget(track, name, options.workerID, options.title, options.naancont)
159
+ if target { // new naancont on existing target
160
+ target.updateController(options)
161
+ track.update(target)
162
+ return (list(false, target))
163
+ }
159
164
 
160
- worker = xnew(window.Worker, "env_webworker.js")
165
+ if options.worker
166
+ worker = options.worker
167
+ else
168
+ worker = xnew(window.Worker, "env_webworker.js")
161
169
  wowbot = WowBot(worker, {
162
170
  name: name
163
171
  type: options.workerID
@@ -172,6 +180,91 @@ closure TermLocal(track, name, options, local target, worker, wowbot, error)
172
180
  if error
173
181
  return (list(Error("cannot create InTop", error)))
174
182
 
183
+ target.title = options.title
184
+ target.naancont = options.naancont
185
+
186
+ // updateController
187
+ //
188
+ // Our controller was replaced, meaning the window changed.
189
+ //
190
+ target.updateController = function updateController(newops) {
191
+ target.execReset()
192
+ target.statusPoll = false
193
+ wowbot.close()
194
+ if defined(newops, `worker) {
195
+ if worker
196
+ target.impReceive({
197
+ id: "textout"
198
+ text: "\x1b[90m\x1b[3m".concat("\ntarget replaced", "\x1b[0m\n")
199
+ })
200
+ worker = newops.worker
201
+ }
202
+ if defined(newops, `title)
203
+ target.title = newops.title
204
+ if defined(newops, `naancont)
205
+ target.naancont = newops.naancont
206
+ wowbot = WowBot(worker, {
207
+ name: name
208
+ type: options.workerID
209
+ startup: options.startup
210
+ })
211
+ target.open(wowbot.instanceID)
212
+ }
213
+
214
+ // targetClose
215
+ //
216
+ // Mark the target window as closed.
217
+ //
218
+ target.targetClose = function targetClose() {
219
+ worker = false
220
+ target.execReset()
221
+ target.cleanup()
222
+ target.impReceive({
223
+ id: "textout"
224
+ text: "\x1b[90m\x1b[3m".concat("\nwindow closed", "\x1b[0m\n")
225
+ })
226
+ target.close()
227
+ }
228
+
229
+ // cleanup
230
+ //
231
+ // InTop is destroying our worker.
232
+ //
233
+ target.cleanup = function cleanup() {
234
+ target.statusPoll = false
235
+ target.naancont = false
236
+ }
237
+
238
+ // setCheckStatus
239
+ //
240
+ // Begin or end checking status on the worker to see if stopped responding. The challenge
241
+ // here is that we open vsite windows in the main thread, but there is no reliable event to learn
242
+ // when they are closed. The solution is to poll the window.closed variable, which is reliable.
243
+ // to learn more quickly if the window as closed, the child may also inform us, but this is not
244
+ // entirely reliable--just quick if accurate.
245
+ //
246
+ target.setCheckStatus = closure setCheckStatus(hidden, window) {
247
+ if !target.statusPoll
248
+ target.statusPoll = future(function childWindowClosePoll() {
249
+ while target.statusPoll {
250
+ if window.closed {
251
+ targetClose()
252
+ target.statusPoll = false
253
+ break
254
+ } else
255
+ sleep(1000) // check 1x/second
256
+ }
257
+ }, 10) // start after 10 msec
258
+ }
259
+
260
+ // vsiteRefresh
261
+ //
262
+ // The vsite upon which we are based has changed.
263
+ //
264
+ target.vsiteRefresh = function vsiteRefresh(url_origin) {
265
+ target.naancont.VsiteRefresh(url_origin)
266
+ }
267
+
175
268
  // finis
176
269
 
177
270
  list(false, target)
@@ -203,27 +296,21 @@ closure findIDEtarget(track, name, workerID, title, naancont, local target) {
203
296
  *
204
297
  * Make an IDE debugger target. The Naan controller in this case, env_web.js, iterates through
205
298
  * all connected terminals so we just have to provide unique connection object for it to talk with.
206
- * This is used for both the IDE itself as well as any additional virtual website windows.
299
+ * This is used for just the IDE itself.
207
300
  *
208
301
  */
209
302
 
210
- closure termIDE(track, name, workerID, naancont, title, local target, connected) {
303
+ closure termIDE(track, name, workerID, naancont, local target, connected) {
211
304
  global(js)
212
- target = findIDEtarget(track, name, workerID, title, naancont)
213
- if target { // new naancont on existing target
214
- if target.naancont && naancont !== target.naancont
215
- target.targetClose()
216
- target.updateController(naancont)
217
- target.title = title
218
- track.update(target)
219
- return (target)
220
- }
305
+ target = findIDEtarget(track, name, workerID)
306
+ if target
307
+ return (list(false, target))
221
308
  target = require("../running/executors.nlg").ExecutorBase(track, "Local", name)
222
309
  target.dispatcher = require("../running/taskexec.nlg").TaskDispatcher(false, false, "termIDE")
223
310
  target.name = name
224
- target.title = title
225
311
  target.workerID = workerID
226
312
  target.naancont = naancont
313
+ target.jsterms = new(weakmap)
227
314
  naancont.ioctl_open(ioctl_callback.proc)
228
315
  connected = []
229
316
 
@@ -265,8 +352,11 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
265
352
  // Create a closure for an attaching terminal.
266
353
  //
267
354
  target.termAttach = closure termAttach(term, db, notify, local connection, jsterm, inbound) {
268
- connection = new(object, this)
269
355
  jsterm = term.jsAcquire() // termIDE must be direct
356
+ if target.jsterms[jsterm]
357
+ return // return existing connection
358
+ connection = new(object, this)
359
+ target.jsterms[jsterm] = connection
270
360
 
271
361
  function oninterrupt() {
272
362
  naancont.Interrupt(inbound)
@@ -301,10 +391,13 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
301
391
  }
302
392
 
303
393
  connection.termDetach = function termDetach() { // disconnect the terminal
394
+ if !target.jsterms[jsterm]
395
+ return // already released
304
396
  db.detach(target)
305
397
  if inbound
306
398
  naancont.Detach(inbound)
307
399
  connected = connected.filter(function(conn) { conn !== connection })
400
+ target.jsterms[jsterm] = undefined
308
401
  term.jsRelease()
309
402
  }
310
403
 
@@ -362,74 +455,6 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
362
455
  connection
363
456
  }
364
457
 
365
- // updateController
366
- //
367
- // Update the naan controller on this target, which means updating for all the terminals.
368
- //
369
- target.updateController = function updateController(newnc, local conn) {
370
- naancont.ioctl_close()
371
- naancont = newnc
372
- naancont.ioctl_open(ioctl_callback.proc)
373
- target.naancont = newnc
374
- for conn in connected {
375
- if naancont {
376
- naancont.Attach(conn.inbound) // tell the controller about terminals
377
- conn.db.attach(target) // re-attach since remote changed
378
- conn.termActive()
379
- } else
380
- conn.db.detach(target) // nothing left to debug
381
- }
382
- target.execReset() // reset our parent class
383
- }
384
-
385
- // targetClose
386
- //
387
- // Mark the target window as closed.
388
- //
389
- target.targetClose = function targetClose(local conn) {
390
- for conn in connected {
391
- conn.db.detach(target)
392
- conn.WriteLn("\x1b[90m\x1b[3m".concat("\ntarget closed", "\x1b[0m"))
393
- }
394
- target.execReset()
395
- naancont.ioctl_close()
396
- target.naancont = naancont = false
397
- }
398
-
399
- // setCheckStatus
400
- //
401
- // Begin or end checking status on the worker to see if stopped responding. The challenge
402
- // here is that we open vsite windows in the main thread, but there is no reliable event to learn
403
- // when they are closed. The solution is to start checking if the window is closed after it gets
404
- // a visibilityChange event to hidden. This terminates when the window gets marked closed or when
405
- // it becomes visible again--which is quite unlikely unless someone uses the explicit hide/show
406
- // window methods.
407
- //
408
- target.setCheckStatus = closure setCheckStatus(hidden, window) {
409
- if hidden {
410
- if !target.statusPoll
411
- target.statusPoll = future(function() {
412
- while target.statusPoll {
413
- if window.closed {
414
- targetClose()
415
- target.statusPoll = false
416
- break
417
- } else
418
- sleep(100) // check 10x/second
419
- }
420
- }, 10) // start after 10 msec
421
- } else
422
- target.statusPoll = false
423
- }
424
-
425
- // vsiteRefresh
426
- //
427
- // The vsite upon which we are based has changed.
428
- //
429
- target.vsiteRefresh = function vsiteRefresh(url_origin) {
430
- naancont.VsiteRefresh(url_origin)
431
- }
432
-
433
458
  // destroy
434
459
  //
435
460
  // Destroy the execution instance.
@@ -460,13 +485,15 @@ closure termInstallVirtualWatcher(track, naancont) {
460
485
 
461
486
  function watchVsites(msg, local error, target) {
462
487
  if msg.op == "VsiteOpen" {
463
- `(error, target) = termIDE(track, msg.name, "NaanVsite", msg.naancont, msg.title)
488
+ `(error, target) = TermLocal(track, msg.name, {
489
+ workerID: "NaanVsite", worker: msg.window, title: msg.title, naancont: msg.naancont
490
+ })
464
491
  target.attention()
465
492
  }
466
493
  else if msg.op == "VsiteClose" {
467
494
  target = findIDEtarget(track, msg.name, "NaanVsite", msg.title, msg.naancont)
468
495
  if target
469
- target.updateController()
496
+ target.updateController({ naancont: false }) // allow re-use of instance
470
497
  }
471
498
  else if msg.op == "VsiteVisibilityChange" {
472
499
  target = findIDEtarget(track, msg.name, "NaanVsite", msg.title, msg.naancont)
@@ -49,7 +49,7 @@ closure InBot(worker, options, local inbot)
49
49
  inbot.workerID = options.workerID
50
50
  inbot.worker = worker
51
51
 
52
- inbot.instanceID = UUID()
52
+ inbot.instanceID = App.imp.getUUID("InBot(${options.name}) instanceID")
53
53
  inbot.sender = App.imp.register(inbot, {
54
54
  name: options.name
55
55
  type: options.type
@@ -133,9 +133,9 @@ closure InBot(worker, options, local inbot)
133
133
  // A message was received from the worker.
134
134
  //
135
135
  inbot.worker.addEventListener("message", function(e, local msg) {
136
- msg = new(e.data)
137
- if msg.scope == "naan-imp-web" {
138
- inbot.impconn.dispatch(msg.data)
136
+ msg = e.data
137
+ if msg.scope == "naan-imp-out" {
138
+ inbot.impconn.dispatch(new(msg.data))
139
139
  return
140
140
  }
141
141
  if msg.scope != "naan-env-web"
@@ -177,6 +177,16 @@ closure InBot(worker, options, local inbot)
177
177
  destroy()
178
178
  })
179
179
 
180
+ // close
181
+ //
182
+ // Close and remove our IMP resources, but leave the worker alone.
183
+ //
184
+ inbot.close = function close() {
185
+ inbot.impconn.destroy()
186
+ inbot.impconn = false
187
+ App.imp.unregister(inbot.instanceID)
188
+ }
189
+
180
190
  // destroy
181
191
  //
182
192
  // Ensure the worker is destroyed and notify everyone.
@@ -189,9 +199,7 @@ closure InBot(worker, options, local inbot)
189
199
  notifyAll({
190
200
  id: "terminated"
191
201
  })
192
- inbot.impconn.destroy()
193
- inbot.impconn = false
194
- App.imp.unregister(inbot.instanceID)
202
+ close()
195
203
  }
196
204
 
197
205
  // finis
@@ -261,7 +269,7 @@ closure WowBot(worker, options, local wowbot)
261
269
 
262
270
  wowbot.impconn = App.imp.responder(function(data) {
263
271
  wowbot.worker.postMessage({ // send IMP messages to worker
264
- scope: "naan-imp-web" // scoped for WebWorkerNub
272
+ scope: "naan-imp-in" // scoped for WebWorkerNub
265
273
  data: data
266
274
  })
267
275
  list(false, { sent: true })
@@ -151,7 +151,18 @@ closure ServiceWorker(pubID, pubVersion, local serv, callback) {
151
151
  message: "opened"
152
152
  })
153
153
  }
154
-
154
+
155
+ // update
156
+ //
157
+ // Go get the latest version of the service worker and update if changed.
158
+ //
159
+ serv.update = closure update(local err, reg) {
160
+ `(err, reg) = await(serv.reg.update())
161
+ if err
162
+ return (list(Error("service worker update failed", err)))
163
+ list(false, reg)
164
+ }
165
+
155
166
  // reload
156
167
  //
157
168
  // If a sevice worker is waiting, then terminate the old one and activate the new one -- which
@@ -13,107 +13,43 @@
13
13
  /*
14
14
  * WebSocket
15
15
  *
16
- * Create a WebSocket client connection object.
16
+ * Create a WebSocket client connection object, returning a tuple.
17
17
  *
18
- * Optional, default Host:
19
- * host: <string> // host:port
18
+ * Required:
19
+ * urlstr: <string>
20
20
  *
21
21
  * Options:
22
22
  * {
23
23
  * wss: <boolean> // true to use secure connection
24
+ * query: <string> // query string starting with ?
24
25
  * onopen: <proc>(e) // connection has opened
25
26
  * onmessage: <proc>(e, message) // message received
26
27
  * onerror: <proc>(e) // error occurred
27
28
  * onclose: <proc>(e) // connection has closed
28
- * query: <string> // query string starting with ? for insecure connections
29
- * querys: <string> // query string starting with ? for secure connections
30
29
  * }
31
30
  *
32
31
  */
33
32
 
34
- closure WebSocket(host, options, local wsock) {
35
- global(window)
36
- wsock = new(object, this)
37
- options = new(options) || { }
38
- if !host {
39
- host = window.location.host
40
- options.wss = window.location.protocol == "https:"
33
+ closure WebSocket(urlstr, options, local url, ws) {
34
+ try {
35
+ url = xnew(js.w.URL, urlstr)
36
+ } catch {
37
+ return (list(Error("invalid URL ${urlstr}", exception)))
41
38
  }
42
-
43
- // connect
44
- //
45
- // Connect with the host if not already connected, returning a result tuple.
46
- //
47
- wsock.connect = closure connect(local url, pending) {
48
- if wsock.ws
49
- return (list(false, { open: true }))
50
- if options.wss
51
- url = "wss://${host}${options.querys||''}"
52
- else
53
- url = "ws://${host}${options.query||''}"
54
- wsock.ws = xnew(window.WebSocket, url)
55
- pending = new(nonce)
56
-
57
- // onopen
58
- // The WebSocket connection is now open.
59
- wsock.ws.onopen = closure onopen(e) {
60
- pending.signal(list(false, { ok: true }))
61
- options.onopen(e)
62
- }
63
-
64
- // onmessage
65
- // A message was received from the worker via WebSockets.
66
- wsock.ws.onmessage = closure onmessage(e, local message) {
67
- try {
68
- message = new(JSONparse(e.data))
69
- } catch {
70
- debuglog("can't decode incoming ws message:", exception)
71
- options.onerror(Error("can't decode incoming message:", exception))
72
- return
73
- }
74
- options.onmessage(e, message)
75
- }
76
-
77
- // onerror
78
- // An error occurred on the WebSocket connection.
79
- wsock.ws.onerror = closure onerror(e, local error) {
80
- error = Error("websocket failed", e)
81
- options.onerror(e, error)
82
- pending.signal(list(error)) // ensure connect completes
83
- }
84
-
85
- // onclose
86
- // The WebSocket connection was closed.
87
- wsock.ws.onclose = closure onclose(e) {
88
- options.onclose(e, e.code, e.reason)
89
- pending.signal(list(Error("connection closed"))) // ensure connect completes
90
- wsock.ws = false
91
- }
92
-
93
- pending.wait()
94
- }
95
-
96
- // send
97
- //
98
- // Send a message dictionary.
99
- //
100
- wsock.send = function send(message) {
101
- wsock.ws.send(JSONstringify(message))
102
- }
103
-
104
- // close
105
- //
106
- // Close the connection.
107
- //
108
- wsock.close = function close(reason) {
109
- if !reason
110
- reason = 1000 // normal close
111
- wsock.ws.close(reason)
112
- }
113
-
114
- // finis
115
-
116
- wsock
39
+ options = merge({
40
+ query: url.search || ''
41
+ wss: url.protocol == "https:"
42
+ }, options)
43
+ if options.wss
44
+ url = "wss://${url.host}${options.query}"
45
+ else
46
+ url = "ws://${url.host}${options.query}"
47
+ ws = xnew(window.WebSocket, url)
48
+ ws.onopen = options.onopen
49
+ ws.onmessage = options.onmessage
50
+ ws.onerror = options.onerror
51
+ ws.onclose = options.onclose
52
+ list(false, ws)
117
53
  };
118
54
 
119
55