lively 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 193f180461ac23de0fd1401f41e1c3b5c09b5d770326b7c9bab0958e7a3ad1f8
4
- data.tar.gz: 716af747f77e13785c2753a0710282828dbfc4d436583fe9b4d53747eb5f2f19
3
+ metadata.gz: 2eb71ca34e43090f3b06b4577cc5c29ed82d3282df6fb26c69adb02cc5eff5fe
4
+ data.tar.gz: 7df1288ae0defc42656da59f5a12e92f3619adc9993e54a4b5e55ac7d10cd795
5
5
  SHA512:
6
- metadata.gz: 38e46574582d3439c82f7d6241103ebaea644f8fd2df4e97e63b970952a8a1d7b5aea49556b9635d6dbb17890bf971ab869cabec023671d083c94f28a6cd0b62
7
- data.tar.gz: 9178f2ceeae957d72d85d73590ed2a40edec2de72ba5545048a460bb37596adada9c81ef6844f6e443222e67d2ddc992abfda4fcbdfebfdae36e7fdd5d1535fb
6
+ metadata.gz: dafdbe14c26702f71de2bc519ac398d141ffc2d1b7f2d1b94f32c44602ea9efc6c083282268f48c7d08798269947322d13bc7005d664b02863fb7bf4f041f031
7
+ data.tar.gz: d65fd9f4c15cd6992c67bf2ffb1ee21d4d86d15643b71e092f38f55386f438bea86e1aa1b0a535a1c0fbd48836e6ec99e77575e751eec513f7c74f77d89064f5
checksums.yaml.gz.sig CHANGED
Binary file
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  module Lively
7
- VERSION = "0.3.0"
7
+ VERSION = "0.4.0"
8
8
  end
@@ -1,4 +1,3 @@
1
-
2
1
  import morphdom from 'morphdom';
3
2
 
4
3
  export class Live {
@@ -25,6 +24,39 @@ export class Live {
25
24
  // Track visibility state and connect if required:
26
25
  this.document.addEventListener("visibilitychange", () => this.handleVisibilityChange());
27
26
  this.handleVisibilityChange();
27
+
28
+ // Create a MutationObserver to watch for removed nodes
29
+ this.observer = new this.window.MutationObserver((mutationsList, observer) => {
30
+ for (let mutation of mutationsList) {
31
+ if (mutation.type === 'childList') {
32
+ for (let node of mutation.removedNodes) {
33
+ if (node.classList?.contains('live')) {
34
+ this.unbind(node);
35
+ }
36
+
37
+ // Unbind any child nodes:
38
+ for (let child of node.getElementsByClassName('live')) {
39
+ this.unbind(child);
40
+ }
41
+ }
42
+
43
+ for (let node of mutation.addedNodes) {
44
+ if (node.classList?.contains('live')) {
45
+ this.bind(node);
46
+ }
47
+
48
+ // Bind any child nodes:
49
+ for (let child of node.getElementsByClassName('live')) {
50
+ this.bind(child);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ });
56
+
57
+ this.observer.observe(this.document.body, {childList: true, subtree: true});
58
+
59
+ this.attach();
28
60
  }
29
61
 
30
62
  // -- Connection Handling --
@@ -36,7 +68,7 @@ export class Live {
36
68
 
37
69
  server.onopen = () => {
38
70
  this.failures = 0;
39
- this.attach();
71
+ this.flush();
40
72
  };
41
73
 
42
74
  server.onmessage = (message) => {
@@ -73,11 +105,15 @@ export class Live {
73
105
  }
74
106
 
75
107
  send(message) {
76
- try {
77
- this.server.send(message);
78
- } catch (error) {
79
- this.events.push(message);
108
+ if (this.server) {
109
+ try {
110
+ return this.server.send(message);
111
+ } catch (error) {
112
+ // Ignore.
113
+ }
80
114
  }
115
+
116
+ this.events.push(message);
81
117
  }
82
118
 
83
119
  flush() {
@@ -91,20 +127,6 @@ export class Live {
91
127
  }
92
128
  }
93
129
 
94
- bind(elements) {
95
- for (var element of elements) {
96
- this.send(JSON.stringify({bind: element.id, data: element.dataset}));
97
- }
98
- }
99
-
100
- bindElementsByClassName(selector = 'live') {
101
- this.bind(
102
- this.document.getElementsByClassName(selector)
103
- );
104
-
105
- this.flush();
106
- }
107
-
108
130
  handleVisibilityChange() {
109
131
  if (this.document.hidden) {
110
132
  this.disconnect();
@@ -113,11 +135,21 @@ export class Live {
113
135
  }
114
136
  }
115
137
 
138
+ bind(element) {
139
+ console.log("bind", element.id, element.dataset);
140
+
141
+ this.send(JSON.stringify(['bind', element.id, element.dataset]));
142
+ }
143
+
144
+ unbind(element) {
145
+ console.log("unbind", element.id, element.dataset);
146
+
147
+ this.send(JSON.stringify(['unbind', element.id]));
148
+ }
149
+
116
150
  attach() {
117
- if (this.document.readyState === 'loading') {
118
- this.document.addEventListener('DOMContentLoaded', () => this.bindElementsByClassName());
119
- } else {
120
- this.bindElementsByClassName();
151
+ for (let node of this.document.getElementsByClassName('live')) {
152
+ this.bind(node);
121
153
  }
122
154
  }
123
155
 
@@ -126,8 +158,8 @@ export class Live {
126
158
  }
127
159
 
128
160
  reply(options) {
129
- if (options && options.reply) {
130
- this.send(JSON.stringify({reply: options.reply}));
161
+ if (options?.reply) {
162
+ this.send(JSON.stringify(['reply', options.reply]));
131
163
  }
132
164
  }
133
165
 
@@ -193,7 +225,7 @@ export class Live {
193
225
  this.connect();
194
226
 
195
227
  this.send(
196
- JSON.stringify({id: id, event: event})
228
+ JSON.stringify(['event', id, event])
197
229
  );
198
230
  }
199
231
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@socketry/live",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.10.0",
5
5
  "description": "Live HTML tags for Ruby.",
6
6
  "main": "Live.js",
7
7
  "repository": {
@@ -1,5 +1,5 @@
1
1
  import {describe, before, after, it} from 'node:test';
2
- import {ok, strict, strictEqual} from 'node:assert';
2
+ import {ok, strict, strictEqual, deepStrictEqual} from 'node:assert';
3
3
 
4
4
  import {WebSocket} from 'ws';
5
5
  import {JSDOM} from 'jsdom';
@@ -13,8 +13,9 @@ describe('Live', function () {
13
13
  const webSocketServerURL = `ws://localhost:${webSocketServerConfig.port}/live`;
14
14
 
15
15
  before(async function () {
16
- const listening = new Promise(resolve => {
16
+ const listening = new Promise((resolve, reject) => {
17
17
  webSocketServer = new WebSocket.Server(webSocketServerConfig, resolve);
18
+ webSocketServer.on('error', reject);
18
19
  });
19
20
 
20
21
  dom = new JSDOM('<!DOCTYPE html><html><body><div id="my"><p>Hello World</p></div></body></html>');
@@ -81,7 +82,7 @@ describe('Live', function () {
81
82
  const reply = new Promise((resolve, reject) => {
82
83
  socket.on('message', message => {
83
84
  let payload = JSON.parse(message);
84
- if (payload.reply) resolve(payload);
85
+ if (payload[0] == 'reply') resolve(payload);
85
86
  });
86
87
  });
87
88
 
@@ -96,7 +97,72 @@ describe('Live', function () {
96
97
  live.disconnect();
97
98
  });
98
99
 
100
+ it('should handle updates with child live elements', async function () {
101
+ const live = new Live(dom.window, webSocketServerURL);
102
+
103
+ live.connect();
104
+
105
+ const connected = new Promise(resolve => {
106
+ webSocketServer.on('connection', resolve);
107
+ });
108
+
109
+ let socket = await connected;
110
+
111
+ const reply = new Promise((resolve, reject) => {
112
+ socket.on('message', message => {
113
+ let payload = JSON.parse(message);
114
+ console.log("message", payload);
115
+ if (payload[0] == 'bind') resolve(payload);
116
+ else console.log("ignoring", payload);
117
+ });
118
+ });
119
+
120
+ socket.send(
121
+ JSON.stringify(['update', 'my', '<div id="my"><div id="mychild" class="live"></div></div>'])
122
+ );
123
+
124
+ let payload = await reply;
125
+
126
+ deepStrictEqual(payload, ['bind', 'mychild', {}]);
127
+
128
+ live.disconnect();
129
+ });
130
+
131
+ it('can unbind removed elements', async function () {
132
+ dom.window.document.body.innerHTML = '<div id="my" class="live"><p>Hello World</p></div>';
133
+
134
+ const live = new Live(dom.window, webSocketServerURL);
135
+
136
+ live.connect();
137
+
138
+ const connected = new Promise(resolve => {
139
+ webSocketServer.on('connection', resolve);
140
+ });
141
+
142
+ let socket = await connected;
143
+
144
+ const reply = new Promise((resolve, reject) => {
145
+ socket.on('message', message => {
146
+ let payload = JSON.parse(message);
147
+ if (payload[0] == 'unbind') resolve(payload);
148
+ else console.log("ignoring", payload);
149
+ });
150
+ });
151
+
152
+ live.attach();
153
+
154
+ dom.window.document.getElementById('my').remove();
155
+
156
+ let payload = await reply;
157
+
158
+ deepStrictEqual(payload, ['unbind', 'my']);
159
+
160
+ live.disconnect();
161
+ });
162
+
99
163
  it('should handle replacements', async function () {
164
+ dom.window.document.body.innerHTML = '<div id="my"><p>Hello World</p></div>';
165
+
100
166
  const live = new Live(dom.window, webSocketServerURL);
101
167
 
102
168
  live.connect();
@@ -110,7 +176,8 @@ describe('Live', function () {
110
176
  const reply = new Promise((resolve, reject) => {
111
177
  socket.on('message', message => {
112
178
  let payload = JSON.parse(message);
113
- if (payload.reply) resolve(payload);
179
+ if (payload[0] == 'reply') resolve(payload);
180
+ else console.log("ignoring", payload);
114
181
  });
115
182
  });
116
183
 
@@ -143,7 +210,8 @@ describe('Live', function () {
143
210
  const reply = new Promise((resolve, reject) => {
144
211
  socket.on('message', message => {
145
212
  let payload = JSON.parse(message);
146
- if (payload.reply) resolve(payload);
213
+ if (payload[0] == 'reply') resolve(payload);
214
+ else console.log("ignoring", payload);
147
215
  });
148
216
  });
149
217
 
@@ -176,7 +244,8 @@ describe('Live', function () {
176
244
  const reply = new Promise((resolve, reject) => {
177
245
  socket.on('message', message => {
178
246
  let payload = JSON.parse(message);
179
- if (payload.reply) resolve(payload);
247
+ if (payload[0] == 'reply') resolve(payload);
248
+ else console.log("ignoring", payload);
180
249
  });
181
250
  });
182
251
 
@@ -209,7 +278,7 @@ describe('Live', function () {
209
278
  const reply = new Promise((resolve, reject) => {
210
279
  socket.on('message', message => {
211
280
  let payload = JSON.parse(message);
212
- if (payload.reply) resolve(payload);
281
+ if (payload[0] == 'reply') resolve(payload);
213
282
  });
214
283
  });
215
284
 
@@ -238,7 +307,7 @@ describe('Live', function () {
238
307
  const reply = new Promise((resolve, reject) => {
239
308
  socket.on('message', message => {
240
309
  let payload = JSON.parse(message);
241
- if (payload.reply) resolve(payload);
310
+ if (payload[0] == 'reply') resolve(payload);
242
311
  });
243
312
  });
244
313
 
@@ -265,7 +334,7 @@ describe('Live', function () {
265
334
  const reply = new Promise((resolve, reject) => {
266
335
  socket.on('message', message => {
267
336
  let payload = JSON.parse(message);
268
- if (payload.event) resolve(payload);
337
+ if (payload[0] == 'event') resolve(payload);
269
338
  });
270
339
  });
271
340
 
@@ -277,8 +346,8 @@ describe('Live', function () {
277
346
 
278
347
  let payload = await reply;
279
348
 
280
- strictEqual(payload.id, 'my');
281
- strictEqual(payload.event.type, 'click');
349
+ strictEqual(payload[1], 'my');
350
+ strictEqual(payload[2].type, 'click');
282
351
 
283
352
  live.disconnect();
284
353
  });
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lively
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -43,30 +43,30 @@ dependencies:
43
43
  name: falcon
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ">="
46
+ - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '0'
48
+ version: '0.47'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ">="
53
+ - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '0'
55
+ version: '0.47'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: live
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: 0.7.0
62
+ version: '0.8'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: 0.7.0
69
+ version: '0.8'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: xrb
72
72
  requirement: !ruby/object:Gem::Requirement
metadata.gz.sig CHANGED
Binary file