livereload-morph 0.1.4 → 0.1.5

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.
@@ -1,1464 +1,1497 @@
1
- // src/protocol.js
2
- var PROTOCOL_6 = "http://livereload.com/protocols/official-6";
3
- var PROTOCOL_7 = "http://livereload.com/protocols/official-7";
1
+ (() => {
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
7
+ var __toCommonJS = (from) => {
8
+ var entry = __moduleCache.get(from), desc;
9
+ if (entry)
10
+ return entry;
11
+ entry = __defProp({}, "__esModule", { value: true });
12
+ if (from && typeof from === "object" || typeof from === "function")
13
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ }));
17
+ __moduleCache.set(from, entry);
18
+ return entry;
19
+ };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
4
29
 
5
- class ProtocolError {
6
- constructor(reason, data) {
7
- this.message = `LiveReload protocol error (${reason}) after receiving data: "${data}".`;
8
- }
9
- }
30
+ // src/index.js
31
+ var exports_src = {};
32
+ __export(exports_src, {
33
+ default: () => src_default
34
+ });
10
35
 
11
- class Parser {
12
- constructor(handlers) {
13
- this.handlers = handlers;
14
- this.reset();
15
- }
16
- reset() {
17
- this.protocol = null;
36
+ // src/protocol.js
37
+ var PROTOCOL_6 = "http://livereload.com/protocols/official-6";
38
+ var PROTOCOL_7 = "http://livereload.com/protocols/official-7";
39
+
40
+ class ProtocolError {
41
+ constructor(reason, data) {
42
+ this.message = `LiveReload protocol error (${reason}) after receiving data: "${data}".`;
43
+ }
18
44
  }
19
- process(data) {
20
- try {
21
- let message;
22
- if (!this.protocol) {
23
- if (data.match(new RegExp("^!!ver:([\\d.]+)$"))) {
24
- this.protocol = 6;
25
- } else if (message = this._parseMessage(data, ["hello"])) {
26
- if (!message.protocols.length) {
27
- throw new ProtocolError("no protocols specified in handshake message");
28
- } else if (Array.from(message.protocols).includes(PROTOCOL_7)) {
29
- this.protocol = 7;
30
- } else if (Array.from(message.protocols).includes(PROTOCOL_6)) {
45
+
46
+ class Parser {
47
+ constructor(handlers) {
48
+ this.handlers = handlers;
49
+ this.reset();
50
+ }
51
+ reset() {
52
+ this.protocol = null;
53
+ }
54
+ process(data) {
55
+ try {
56
+ let message;
57
+ if (!this.protocol) {
58
+ if (data.match(new RegExp("^!!ver:([\\d.]+)$"))) {
31
59
  this.protocol = 6;
32
- } else {
33
- throw new ProtocolError("no supported protocols found");
60
+ } else if (message = this._parseMessage(data, ["hello"])) {
61
+ if (!message.protocols.length) {
62
+ throw new ProtocolError("no protocols specified in handshake message");
63
+ } else if (Array.from(message.protocols).includes(PROTOCOL_7)) {
64
+ this.protocol = 7;
65
+ } else if (Array.from(message.protocols).includes(PROTOCOL_6)) {
66
+ this.protocol = 6;
67
+ } else {
68
+ throw new ProtocolError("no supported protocols found");
69
+ }
34
70
  }
71
+ return this.handlers.connected(this.protocol);
35
72
  }
36
- return this.handlers.connected(this.protocol);
37
- }
38
- if (this.protocol === 6) {
39
- message = JSON.parse(data);
40
- if (!message.length) {
41
- throw new ProtocolError("protocol 6 messages must be arrays");
73
+ if (this.protocol === 6) {
74
+ message = JSON.parse(data);
75
+ if (!message.length) {
76
+ throw new ProtocolError("protocol 6 messages must be arrays");
77
+ }
78
+ const [command, options] = Array.from(message);
79
+ if (command !== "refresh") {
80
+ throw new ProtocolError("unknown protocol 6 command");
81
+ }
82
+ return this.handlers.message({
83
+ command: "reload",
84
+ path: options.path,
85
+ liveCSS: options.apply_css_live != null ? options.apply_css_live : true
86
+ });
42
87
  }
43
- const [command, options] = Array.from(message);
44
- if (command !== "refresh") {
45
- throw new ProtocolError("unknown protocol 6 command");
88
+ message = this._parseMessage(data, ["reload", "alert"]);
89
+ return this.handlers.message(message);
90
+ } catch (e) {
91
+ if (e instanceof ProtocolError) {
92
+ return this.handlers.error(e);
46
93
  }
47
- return this.handlers.message({
48
- command: "reload",
49
- path: options.path,
50
- liveCSS: options.apply_css_live != null ? options.apply_css_live : true
51
- });
52
- }
53
- message = this._parseMessage(data, ["reload", "alert"]);
54
- return this.handlers.message(message);
55
- } catch (e) {
56
- if (e instanceof ProtocolError) {
57
- return this.handlers.error(e);
94
+ throw e;
58
95
  }
59
- throw e;
60
96
  }
61
- }
62
- _parseMessage(data, validCommands) {
63
- let message;
64
- try {
65
- message = JSON.parse(data);
66
- } catch (e) {
67
- throw new ProtocolError("unparsable JSON", data);
68
- }
69
- if (!message.command) {
70
- throw new ProtocolError('missing "command" key', data);
71
- }
72
- if (!validCommands.includes(message.command)) {
73
- throw new ProtocolError(`invalid command '${message.command}', only valid commands are: ${validCommands.join(", ")})`, data);
97
+ _parseMessage(data, validCommands) {
98
+ let message;
99
+ try {
100
+ message = JSON.parse(data);
101
+ } catch (e) {
102
+ throw new ProtocolError("unparsable JSON", data);
103
+ }
104
+ if (!message.command) {
105
+ throw new ProtocolError('missing "command" key', data);
106
+ }
107
+ if (!validCommands.includes(message.command)) {
108
+ throw new ProtocolError(`invalid command '${message.command}', only valid commands are: ${validCommands.join(", ")})`, data);
109
+ }
110
+ return message;
74
111
  }
75
- return message;
76
112
  }
77
- }
78
113
 
79
- // src/connector.js
80
- var VERSION = "1.0.0";
114
+ // src/connector.js
115
+ var VERSION = "1.0.0";
81
116
 
82
- class Connector {
83
- constructor(options, WebSocket, Timer, handlers) {
84
- this.options = options;
85
- this.WebSocket = WebSocket;
86
- this.Timer = Timer;
87
- this.handlers = handlers;
88
- const path = this.options.path ? `${this.options.path}` : "livereload";
89
- const port = this.options.port ? `:${this.options.port}` : "";
90
- this._uri = `ws${this.options.https ? "s" : ""}://${this.options.host}${port}/${path}`;
91
- this._nextDelay = this.options.mindelay;
92
- this._connectionDesired = false;
93
- this.protocol = 0;
94
- this.protocolParser = new Parser({
95
- connected: (protocol) => {
96
- this.protocol = protocol;
97
- this._handshakeTimeout.stop();
98
- this._nextDelay = this.options.mindelay;
99
- this._disconnectionReason = "broken";
100
- return this.handlers.connected(this.protocol);
101
- },
102
- error: (e) => {
103
- this.handlers.error(e);
104
- return this._closeOnError();
105
- },
106
- message: (message) => {
107
- return this.handlers.message(message);
117
+ class Connector {
118
+ constructor(options, WebSocket, Timer, handlers) {
119
+ this.options = options;
120
+ this.WebSocket = WebSocket;
121
+ this.Timer = Timer;
122
+ this.handlers = handlers;
123
+ const path = this.options.path ? `${this.options.path}` : "livereload";
124
+ const port = this.options.port ? `:${this.options.port}` : "";
125
+ this._uri = `ws${this.options.https ? "s" : ""}://${this.options.host}${port}/${path}`;
126
+ this._nextDelay = this.options.mindelay;
127
+ this._connectionDesired = false;
128
+ this.protocol = 0;
129
+ this.protocolParser = new Parser({
130
+ connected: (protocol) => {
131
+ this.protocol = protocol;
132
+ this._handshakeTimeout.stop();
133
+ this._nextDelay = this.options.mindelay;
134
+ this._disconnectionReason = "broken";
135
+ return this.handlers.connected(this.protocol);
136
+ },
137
+ error: (e) => {
138
+ this.handlers.error(e);
139
+ return this._closeOnError();
140
+ },
141
+ message: (message) => {
142
+ return this.handlers.message(message);
143
+ }
144
+ });
145
+ this._handshakeTimeout = new this.Timer(() => {
146
+ if (!this._isSocketConnected()) {
147
+ return;
148
+ }
149
+ this._disconnectionReason = "handshake-timeout";
150
+ return this.socket.close();
151
+ });
152
+ this._reconnectTimer = new this.Timer(() => {
153
+ if (!this._connectionDesired) {
154
+ return;
155
+ }
156
+ return this.connect();
157
+ });
158
+ this.connect();
159
+ }
160
+ _isSocketConnected() {
161
+ return this.socket && this.socket.readyState === this.WebSocket.OPEN;
162
+ }
163
+ connect() {
164
+ this._connectionDesired = true;
165
+ if (this._isSocketConnected()) {
166
+ return;
108
167
  }
109
- });
110
- this._handshakeTimeout = new this.Timer(() => {
168
+ this._reconnectTimer.stop();
169
+ this._disconnectionReason = "cannot-connect";
170
+ this.protocolParser.reset();
171
+ this.handlers.connecting();
172
+ this.socket = new this.WebSocket(this._uri);
173
+ this.socket.onopen = (e) => this._onopen(e);
174
+ this.socket.onclose = (e) => this._onclose(e);
175
+ this.socket.onmessage = (e) => this._onmessage(e);
176
+ this.socket.onerror = (e) => this._onerror(e);
177
+ }
178
+ disconnect() {
179
+ this._connectionDesired = false;
180
+ this._reconnectTimer.stop();
111
181
  if (!this._isSocketConnected()) {
112
182
  return;
113
183
  }
114
- this._disconnectionReason = "handshake-timeout";
184
+ this._disconnectionReason = "manual";
115
185
  return this.socket.close();
116
- });
117
- this._reconnectTimer = new this.Timer(() => {
186
+ }
187
+ _scheduleReconnection() {
118
188
  if (!this._connectionDesired) {
119
189
  return;
120
190
  }
121
- return this.connect();
122
- });
123
- this.connect();
124
- }
125
- _isSocketConnected() {
126
- return this.socket && this.socket.readyState === this.WebSocket.OPEN;
127
- }
128
- connect() {
129
- this._connectionDesired = true;
130
- if (this._isSocketConnected()) {
131
- return;
191
+ if (!this._reconnectTimer.running) {
192
+ this._reconnectTimer.start(this._nextDelay);
193
+ this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2);
194
+ }
132
195
  }
133
- this._reconnectTimer.stop();
134
- this._disconnectionReason = "cannot-connect";
135
- this.protocolParser.reset();
136
- this.handlers.connecting();
137
- this.socket = new this.WebSocket(this._uri);
138
- this.socket.onopen = (e) => this._onopen(e);
139
- this.socket.onclose = (e) => this._onclose(e);
140
- this.socket.onmessage = (e) => this._onmessage(e);
141
- this.socket.onerror = (e) => this._onerror(e);
142
- }
143
- disconnect() {
144
- this._connectionDesired = false;
145
- this._reconnectTimer.stop();
146
- if (!this._isSocketConnected()) {
147
- return;
196
+ sendCommand(command) {
197
+ if (!this.protocol) {
198
+ return;
199
+ }
200
+ return this._sendCommand(command);
148
201
  }
149
- this._disconnectionReason = "manual";
150
- return this.socket.close();
151
- }
152
- _scheduleReconnection() {
153
- if (!this._connectionDesired) {
154
- return;
202
+ _sendCommand(command) {
203
+ return this.socket.send(JSON.stringify(command));
155
204
  }
156
- if (!this._reconnectTimer.running) {
157
- this._reconnectTimer.start(this._nextDelay);
158
- this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2);
205
+ _closeOnError() {
206
+ this._handshakeTimeout.stop();
207
+ this._disconnectionReason = "error";
208
+ return this.socket.close();
159
209
  }
160
- }
161
- sendCommand(command) {
162
- if (!this.protocol) {
163
- return;
210
+ _onopen(e) {
211
+ this.handlers.socketConnected();
212
+ this._disconnectionReason = "handshake-failed";
213
+ const hello = {
214
+ command: "hello",
215
+ protocols: [PROTOCOL_6, PROTOCOL_7]
216
+ };
217
+ hello.ver = VERSION;
218
+ this._sendCommand(hello);
219
+ return this._handshakeTimeout.start(this.options.handshake_timeout);
164
220
  }
165
- return this._sendCommand(command);
166
- }
167
- _sendCommand(command) {
168
- return this.socket.send(JSON.stringify(command));
169
- }
170
- _closeOnError() {
171
- this._handshakeTimeout.stop();
172
- this._disconnectionReason = "error";
173
- return this.socket.close();
174
- }
175
- _onopen(e) {
176
- this.handlers.socketConnected();
177
- this._disconnectionReason = "handshake-failed";
178
- const hello = {
179
- command: "hello",
180
- protocols: [PROTOCOL_6, PROTOCOL_7]
181
- };
182
- hello.ver = VERSION;
183
- this._sendCommand(hello);
184
- return this._handshakeTimeout.start(this.options.handshake_timeout);
185
- }
186
- _onclose(e) {
187
- this.protocol = 0;
188
- this.handlers.disconnected(this._disconnectionReason, this._nextDelay);
189
- return this._scheduleReconnection();
190
- }
191
- _onerror(e) {}
192
- _onmessage(e) {
193
- return this.protocolParser.process(e.data);
194
- }
195
- }
196
-
197
- // src/timer.js
198
- class Timer {
199
- constructor(func) {
200
- this.func = func;
201
- this.running = false;
202
- this.id = null;
203
- this._handler = () => {
204
- this.running = false;
205
- this.id = null;
206
- return this.func();
207
- };
208
- }
209
- start(timeout) {
210
- if (this.running) {
211
- clearTimeout(this.id);
221
+ _onclose(e) {
222
+ this.protocol = 0;
223
+ this.handlers.disconnected(this._disconnectionReason, this._nextDelay);
224
+ return this._scheduleReconnection();
225
+ }
226
+ _onerror(e) {}
227
+ _onmessage(e) {
228
+ return this.protocolParser.process(e.data);
212
229
  }
213
- this.id = setTimeout(this._handler, timeout);
214
- this.running = true;
215
230
  }
216
- stop() {
217
- if (this.running) {
218
- clearTimeout(this.id);
231
+
232
+ // src/timer.js
233
+ class Timer {
234
+ constructor(func) {
235
+ this.func = func;
219
236
  this.running = false;
220
237
  this.id = null;
238
+ this._handler = () => {
239
+ this.running = false;
240
+ this.id = null;
241
+ return this.func();
242
+ };
221
243
  }
222
- }
223
- }
224
- Timer.start = (timeout, func) => setTimeout(func, timeout);
225
-
226
- // src/options.js
227
- class Options {
228
- constructor() {
229
- this.https = false;
230
- this.host = null;
231
- let port = 35729;
232
- Object.defineProperty(this, "port", {
233
- get() {
234
- return port;
235
- },
236
- set(v) {
237
- port = v ? isNaN(v) ? v : +v : "";
244
+ start(timeout) {
245
+ if (this.running) {
246
+ clearTimeout(this.id);
238
247
  }
239
- });
240
- this.mindelay = 1000;
241
- this.maxdelay = 60000;
242
- this.handshake_timeout = 5000;
243
- this.morphHTML = true;
244
- this.verbose = false;
245
- this.importCacheWaitPeriod = 200;
246
- }
247
- set(name, value) {
248
- if (typeof value === "undefined") {
249
- return;
250
- }
251
- if (!isNaN(+value)) {
252
- value = +value;
248
+ this.id = setTimeout(this._handler, timeout);
249
+ this.running = true;
253
250
  }
254
- if (value === "true") {
255
- value = true;
256
- } else if (value === "false") {
257
- value = false;
251
+ stop() {
252
+ if (this.running) {
253
+ clearTimeout(this.id);
254
+ this.running = false;
255
+ this.id = null;
256
+ }
258
257
  }
259
- this[name] = value;
260
258
  }
261
- }
262
- Options.extract = function(document2) {
263
- const win = document2.defaultView || window;
264
- if (win && win.LiveMorphOptions) {
265
- const options = new Options;
266
- for (const [key, value] of Object.entries(win.LiveMorphOptions)) {
267
- options.set(key, value);
259
+ Timer.start = (timeout, func) => setTimeout(func, timeout);
260
+
261
+ // src/options.js
262
+ class Options {
263
+ constructor() {
264
+ this.https = false;
265
+ this.host = null;
266
+ let port = 35729;
267
+ Object.defineProperty(this, "port", {
268
+ get() {
269
+ return port;
270
+ },
271
+ set(v) {
272
+ port = v ? isNaN(v) ? v : +v : "";
273
+ }
274
+ });
275
+ this.mindelay = 1000;
276
+ this.maxdelay = 60000;
277
+ this.handshake_timeout = 5000;
278
+ this.morphHTML = true;
279
+ this.verbose = false;
280
+ this.importCacheWaitPeriod = 200;
281
+ }
282
+ set(name, value) {
283
+ if (typeof value === "undefined") {
284
+ return;
285
+ }
286
+ if (!isNaN(+value)) {
287
+ value = +value;
288
+ }
289
+ if (value === "true") {
290
+ value = true;
291
+ } else if (value === "false") {
292
+ value = false;
293
+ }
294
+ this[name] = value;
268
295
  }
269
- return options;
270
296
  }
271
- const scripts = Array.from(document2.getElementsByTagName("script"));
272
- for (const script of scripts) {
273
- const host = script.getAttribute("data-livereload-morph-host");
274
- if (host) {
297
+ Options.extract = function(document2) {
298
+ const win = document2.defaultView || window;
299
+ if (win && win.LiveMorphOptions) {
275
300
  const options = new Options;
276
- options.host = host;
277
- const port = script.getAttribute("data-livereload-morph-port");
278
- if (port)
279
- options.port = parseInt(port, 10);
280
- const verbose = script.getAttribute("data-livereload-morph-verbose");
281
- if (verbose !== null)
282
- options.verbose = verbose === "true";
301
+ for (const [key, value] of Object.entries(win.LiveMorphOptions)) {
302
+ options.set(key, value);
303
+ }
283
304
  return options;
284
305
  }
285
- }
286
- return null;
287
- };
288
-
289
- // node_modules/idiomorph/dist/idiomorph.esm.js
290
- var Idiomorph = function() {
291
- const noOp = () => {};
292
- const defaults = {
293
- morphStyle: "outerHTML",
294
- callbacks: {
295
- beforeNodeAdded: noOp,
296
- afterNodeAdded: noOp,
297
- beforeNodeMorphed: noOp,
298
- afterNodeMorphed: noOp,
299
- beforeNodeRemoved: noOp,
300
- afterNodeRemoved: noOp,
301
- beforeAttributeUpdated: noOp
302
- },
303
- head: {
304
- style: "merge",
305
- shouldPreserve: (elt) => elt.getAttribute("im-preserve") === "true",
306
- shouldReAppend: (elt) => elt.getAttribute("im-re-append") === "true",
307
- shouldRemove: noOp,
308
- afterHeadMorphed: noOp
309
- },
310
- restoreFocus: true
306
+ const scripts = Array.from(document2.getElementsByTagName("script"));
307
+ for (const script of scripts) {
308
+ const host = script.getAttribute("data-livereload-morph-host");
309
+ if (host) {
310
+ const options = new Options;
311
+ options.host = host;
312
+ const port = script.getAttribute("data-livereload-morph-port");
313
+ if (port)
314
+ options.port = parseInt(port, 10);
315
+ const verbose = script.getAttribute("data-livereload-morph-verbose");
316
+ if (verbose !== null)
317
+ options.verbose = verbose === "true";
318
+ return options;
319
+ }
320
+ }
321
+ return null;
311
322
  };
312
- function morph(oldNode, newContent, config = {}) {
313
- oldNode = normalizeElement(oldNode);
314
- const newNode = normalizeParent(newContent);
315
- const ctx = createMorphContext(oldNode, newNode, config);
316
- const morphedNodes = saveAndRestoreFocus(ctx, () => {
317
- return withHeadBlocking(ctx, oldNode, newNode, (ctx2) => {
318
- if (ctx2.morphStyle === "innerHTML") {
319
- morphChildren(ctx2, oldNode, newNode);
320
- return Array.from(oldNode.childNodes);
321
- } else {
322
- return morphOuterHTML(ctx2, oldNode, newNode);
323
- }
323
+
324
+ // node_modules/idiomorph/dist/idiomorph.esm.js
325
+ var Idiomorph = function() {
326
+ const noOp = () => {};
327
+ const defaults = {
328
+ morphStyle: "outerHTML",
329
+ callbacks: {
330
+ beforeNodeAdded: noOp,
331
+ afterNodeAdded: noOp,
332
+ beforeNodeMorphed: noOp,
333
+ afterNodeMorphed: noOp,
334
+ beforeNodeRemoved: noOp,
335
+ afterNodeRemoved: noOp,
336
+ beforeAttributeUpdated: noOp
337
+ },
338
+ head: {
339
+ style: "merge",
340
+ shouldPreserve: (elt) => elt.getAttribute("im-preserve") === "true",
341
+ shouldReAppend: (elt) => elt.getAttribute("im-re-append") === "true",
342
+ shouldRemove: noOp,
343
+ afterHeadMorphed: noOp
344
+ },
345
+ restoreFocus: true
346
+ };
347
+ function morph(oldNode, newContent, config = {}) {
348
+ oldNode = normalizeElement(oldNode);
349
+ const newNode = normalizeParent(newContent);
350
+ const ctx = createMorphContext(oldNode, newNode, config);
351
+ const morphedNodes = saveAndRestoreFocus(ctx, () => {
352
+ return withHeadBlocking(ctx, oldNode, newNode, (ctx2) => {
353
+ if (ctx2.morphStyle === "innerHTML") {
354
+ morphChildren(ctx2, oldNode, newNode);
355
+ return Array.from(oldNode.childNodes);
356
+ } else {
357
+ return morphOuterHTML(ctx2, oldNode, newNode);
358
+ }
359
+ });
324
360
  });
325
- });
326
- ctx.pantry.remove();
327
- return morphedNodes;
328
- }
329
- function morphOuterHTML(ctx, oldNode, newNode) {
330
- const oldParent = normalizeParent(oldNode);
331
- morphChildren(ctx, oldParent, newNode, oldNode, oldNode.nextSibling);
332
- return Array.from(oldParent.childNodes);
333
- }
334
- function saveAndRestoreFocus(ctx, fn) {
335
- if (!ctx.config.restoreFocus)
336
- return fn();
337
- let activeElement = document.activeElement;
338
- if (!(activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) {
339
- return fn();
340
- }
341
- const { id: activeElementId, selectionStart, selectionEnd } = activeElement;
342
- const results = fn();
343
- if (activeElementId && activeElementId !== document.activeElement?.getAttribute("id")) {
344
- activeElement = ctx.target.querySelector(`[id="${activeElementId}"]`);
345
- activeElement?.focus();
346
- }
347
- if (activeElement && !activeElement.selectionEnd && selectionEnd) {
348
- activeElement.setSelectionRange(selectionStart, selectionEnd);
349
- }
350
- return results;
351
- }
352
- const morphChildren = function() {
353
- function morphChildren2(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) {
354
- if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) {
355
- oldParent = oldParent.content;
356
- newParent = newParent.content;
357
- }
358
- insertionPoint ||= oldParent.firstChild;
359
- for (const newChild of newParent.childNodes) {
360
- if (insertionPoint && insertionPoint != endPoint) {
361
- const bestMatch = findBestMatch(ctx, newChild, insertionPoint, endPoint);
362
- if (bestMatch) {
363
- if (bestMatch !== insertionPoint) {
364
- removeNodesBetween(ctx, insertionPoint, bestMatch);
361
+ ctx.pantry.remove();
362
+ return morphedNodes;
363
+ }
364
+ function morphOuterHTML(ctx, oldNode, newNode) {
365
+ const oldParent = normalizeParent(oldNode);
366
+ morphChildren(ctx, oldParent, newNode, oldNode, oldNode.nextSibling);
367
+ return Array.from(oldParent.childNodes);
368
+ }
369
+ function saveAndRestoreFocus(ctx, fn) {
370
+ if (!ctx.config.restoreFocus)
371
+ return fn();
372
+ let activeElement = document.activeElement;
373
+ if (!(activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) {
374
+ return fn();
375
+ }
376
+ const { id: activeElementId, selectionStart, selectionEnd } = activeElement;
377
+ const results = fn();
378
+ if (activeElementId && activeElementId !== document.activeElement?.getAttribute("id")) {
379
+ activeElement = ctx.target.querySelector(`[id="${activeElementId}"]`);
380
+ activeElement?.focus();
381
+ }
382
+ if (activeElement && !activeElement.selectionEnd && selectionEnd) {
383
+ activeElement.setSelectionRange(selectionStart, selectionEnd);
384
+ }
385
+ return results;
386
+ }
387
+ const morphChildren = function() {
388
+ function morphChildren2(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) {
389
+ if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) {
390
+ oldParent = oldParent.content;
391
+ newParent = newParent.content;
392
+ }
393
+ insertionPoint ||= oldParent.firstChild;
394
+ for (const newChild of newParent.childNodes) {
395
+ if (insertionPoint && insertionPoint != endPoint) {
396
+ const bestMatch = findBestMatch(ctx, newChild, insertionPoint, endPoint);
397
+ if (bestMatch) {
398
+ if (bestMatch !== insertionPoint) {
399
+ removeNodesBetween(ctx, insertionPoint, bestMatch);
400
+ }
401
+ morphNode(bestMatch, newChild, ctx);
402
+ insertionPoint = bestMatch.nextSibling;
403
+ continue;
365
404
  }
366
- morphNode(bestMatch, newChild, ctx);
367
- insertionPoint = bestMatch.nextSibling;
368
- continue;
369
405
  }
370
- }
371
- if (newChild instanceof Element) {
372
- const newChildId = newChild.getAttribute("id");
373
- if (ctx.persistentIds.has(newChildId)) {
374
- const movedChild = moveBeforeById(oldParent, newChildId, insertionPoint, ctx);
375
- morphNode(movedChild, newChild, ctx);
376
- insertionPoint = movedChild.nextSibling;
377
- continue;
406
+ if (newChild instanceof Element) {
407
+ const newChildId = newChild.getAttribute("id");
408
+ if (ctx.persistentIds.has(newChildId)) {
409
+ const movedChild = moveBeforeById(oldParent, newChildId, insertionPoint, ctx);
410
+ morphNode(movedChild, newChild, ctx);
411
+ insertionPoint = movedChild.nextSibling;
412
+ continue;
413
+ }
414
+ }
415
+ const insertedNode = createNode(oldParent, newChild, insertionPoint, ctx);
416
+ if (insertedNode) {
417
+ insertionPoint = insertedNode.nextSibling;
378
418
  }
379
419
  }
380
- const insertedNode = createNode(oldParent, newChild, insertionPoint, ctx);
381
- if (insertedNode) {
382
- insertionPoint = insertedNode.nextSibling;
420
+ while (insertionPoint && insertionPoint != endPoint) {
421
+ const tempNode = insertionPoint;
422
+ insertionPoint = insertionPoint.nextSibling;
423
+ removeNode(ctx, tempNode);
383
424
  }
384
425
  }
385
- while (insertionPoint && insertionPoint != endPoint) {
386
- const tempNode = insertionPoint;
387
- insertionPoint = insertionPoint.nextSibling;
388
- removeNode(ctx, tempNode);
389
- }
390
- }
391
- function createNode(oldParent, newChild, insertionPoint, ctx) {
392
- if (ctx.callbacks.beforeNodeAdded(newChild) === false)
393
- return null;
394
- if (ctx.idMap.has(newChild)) {
395
- const newEmptyChild = document.createElement(newChild.tagName);
396
- oldParent.insertBefore(newEmptyChild, insertionPoint);
397
- morphNode(newEmptyChild, newChild, ctx);
398
- ctx.callbacks.afterNodeAdded(newEmptyChild);
399
- return newEmptyChild;
400
- } else {
401
- const newClonedChild = document.importNode(newChild, true);
402
- oldParent.insertBefore(newClonedChild, insertionPoint);
403
- ctx.callbacks.afterNodeAdded(newClonedChild);
404
- return newClonedChild;
426
+ function createNode(oldParent, newChild, insertionPoint, ctx) {
427
+ if (ctx.callbacks.beforeNodeAdded(newChild) === false)
428
+ return null;
429
+ if (ctx.idMap.has(newChild)) {
430
+ const newEmptyChild = document.createElement(newChild.tagName);
431
+ oldParent.insertBefore(newEmptyChild, insertionPoint);
432
+ morphNode(newEmptyChild, newChild, ctx);
433
+ ctx.callbacks.afterNodeAdded(newEmptyChild);
434
+ return newEmptyChild;
435
+ } else {
436
+ const newClonedChild = document.importNode(newChild, true);
437
+ oldParent.insertBefore(newClonedChild, insertionPoint);
438
+ ctx.callbacks.afterNodeAdded(newClonedChild);
439
+ return newClonedChild;
440
+ }
405
441
  }
406
- }
407
- const findBestMatch = function() {
408
- function findBestMatch2(ctx, node, startPoint, endPoint) {
409
- let softMatch = null;
410
- let nextSibling = node.nextSibling;
411
- let siblingSoftMatchCount = 0;
412
- let cursor = startPoint;
413
- while (cursor && cursor != endPoint) {
414
- if (isSoftMatch(cursor, node)) {
415
- if (isIdSetMatch(ctx, cursor, node)) {
416
- return cursor;
442
+ const findBestMatch = function() {
443
+ function findBestMatch2(ctx, node, startPoint, endPoint) {
444
+ let softMatch = null;
445
+ let nextSibling = node.nextSibling;
446
+ let siblingSoftMatchCount = 0;
447
+ let cursor = startPoint;
448
+ while (cursor && cursor != endPoint) {
449
+ if (isSoftMatch(cursor, node)) {
450
+ if (isIdSetMatch(ctx, cursor, node)) {
451
+ return cursor;
452
+ }
453
+ if (softMatch === null) {
454
+ if (!ctx.idMap.has(cursor)) {
455
+ softMatch = cursor;
456
+ }
457
+ }
417
458
  }
418
- if (softMatch === null) {
419
- if (!ctx.idMap.has(cursor)) {
420
- softMatch = cursor;
459
+ if (softMatch === null && nextSibling && isSoftMatch(cursor, nextSibling)) {
460
+ siblingSoftMatchCount++;
461
+ nextSibling = nextSibling.nextSibling;
462
+ if (siblingSoftMatchCount >= 2) {
463
+ softMatch = undefined;
421
464
  }
422
465
  }
466
+ if (ctx.activeElementAndParents.includes(cursor))
467
+ break;
468
+ cursor = cursor.nextSibling;
423
469
  }
424
- if (softMatch === null && nextSibling && isSoftMatch(cursor, nextSibling)) {
425
- siblingSoftMatchCount++;
426
- nextSibling = nextSibling.nextSibling;
427
- if (siblingSoftMatchCount >= 2) {
428
- softMatch = undefined;
470
+ return softMatch || null;
471
+ }
472
+ function isIdSetMatch(ctx, oldNode, newNode) {
473
+ let oldSet = ctx.idMap.get(oldNode);
474
+ let newSet = ctx.idMap.get(newNode);
475
+ if (!newSet || !oldSet)
476
+ return false;
477
+ for (const id of oldSet) {
478
+ if (newSet.has(id)) {
479
+ return true;
429
480
  }
430
481
  }
431
- if (ctx.activeElementAndParents.includes(cursor))
432
- break;
433
- cursor = cursor.nextSibling;
434
- }
435
- return softMatch || null;
436
- }
437
- function isIdSetMatch(ctx, oldNode, newNode) {
438
- let oldSet = ctx.idMap.get(oldNode);
439
- let newSet = ctx.idMap.get(newNode);
440
- if (!newSet || !oldSet)
441
482
  return false;
442
- for (const id of oldSet) {
443
- if (newSet.has(id)) {
444
- return true;
445
- }
446
483
  }
447
- return false;
448
- }
449
- function isSoftMatch(oldNode, newNode) {
450
- const oldElt = oldNode;
451
- const newElt = newNode;
452
- return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && (!oldElt.getAttribute?.("id") || oldElt.getAttribute?.("id") === newElt.getAttribute?.("id"));
453
- }
454
- return findBestMatch2;
455
- }();
456
- function removeNode(ctx, node) {
457
- if (ctx.idMap.has(node)) {
458
- moveBefore(ctx.pantry, node, null);
459
- } else {
460
- if (ctx.callbacks.beforeNodeRemoved(node) === false)
461
- return;
462
- node.parentNode?.removeChild(node);
463
- ctx.callbacks.afterNodeRemoved(node);
484
+ function isSoftMatch(oldNode, newNode) {
485
+ const oldElt = oldNode;
486
+ const newElt = newNode;
487
+ return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && (!oldElt.getAttribute?.("id") || oldElt.getAttribute?.("id") === newElt.getAttribute?.("id"));
488
+ }
489
+ return findBestMatch2;
490
+ }();
491
+ function removeNode(ctx, node) {
492
+ if (ctx.idMap.has(node)) {
493
+ moveBefore(ctx.pantry, node, null);
494
+ } else {
495
+ if (ctx.callbacks.beforeNodeRemoved(node) === false)
496
+ return;
497
+ node.parentNode?.removeChild(node);
498
+ ctx.callbacks.afterNodeRemoved(node);
499
+ }
464
500
  }
465
- }
466
- function removeNodesBetween(ctx, startInclusive, endExclusive) {
467
- let cursor = startInclusive;
468
- while (cursor && cursor !== endExclusive) {
469
- let tempNode = cursor;
470
- cursor = cursor.nextSibling;
471
- removeNode(ctx, tempNode);
472
- }
473
- return cursor;
474
- }
475
- function moveBeforeById(parentNode, id, after, ctx) {
476
- const target = ctx.target.getAttribute?.("id") === id && ctx.target || ctx.target.querySelector(`[id="${id}"]`) || ctx.pantry.querySelector(`[id="${id}"]`);
477
- removeElementFromAncestorsIdMaps(target, ctx);
478
- moveBefore(parentNode, target, after);
479
- return target;
480
- }
481
- function removeElementFromAncestorsIdMaps(element, ctx) {
482
- const id = element.getAttribute("id");
483
- while (element = element.parentNode) {
484
- let idSet = ctx.idMap.get(element);
485
- if (idSet) {
486
- idSet.delete(id);
487
- if (!idSet.size) {
488
- ctx.idMap.delete(element);
501
+ function removeNodesBetween(ctx, startInclusive, endExclusive) {
502
+ let cursor = startInclusive;
503
+ while (cursor && cursor !== endExclusive) {
504
+ let tempNode = cursor;
505
+ cursor = cursor.nextSibling;
506
+ removeNode(ctx, tempNode);
507
+ }
508
+ return cursor;
509
+ }
510
+ function moveBeforeById(parentNode, id, after, ctx) {
511
+ const target = ctx.target.getAttribute?.("id") === id && ctx.target || ctx.target.querySelector(`[id="${id}"]`) || ctx.pantry.querySelector(`[id="${id}"]`);
512
+ removeElementFromAncestorsIdMaps(target, ctx);
513
+ moveBefore(parentNode, target, after);
514
+ return target;
515
+ }
516
+ function removeElementFromAncestorsIdMaps(element, ctx) {
517
+ const id = element.getAttribute("id");
518
+ while (element = element.parentNode) {
519
+ let idSet = ctx.idMap.get(element);
520
+ if (idSet) {
521
+ idSet.delete(id);
522
+ if (!idSet.size) {
523
+ ctx.idMap.delete(element);
524
+ }
489
525
  }
490
526
  }
491
527
  }
492
- }
493
- function moveBefore(parentNode, element, after) {
494
- if (parentNode.moveBefore) {
495
- try {
496
- parentNode.moveBefore(element, after);
497
- } catch (e) {
528
+ function moveBefore(parentNode, element, after) {
529
+ if (parentNode.moveBefore) {
530
+ try {
531
+ parentNode.moveBefore(element, after);
532
+ } catch (e) {
533
+ parentNode.insertBefore(element, after);
534
+ }
535
+ } else {
498
536
  parentNode.insertBefore(element, after);
499
537
  }
500
- } else {
501
- parentNode.insertBefore(element, after);
502
- }
503
- }
504
- return morphChildren2;
505
- }();
506
- const morphNode = function() {
507
- function morphNode2(oldNode, newContent, ctx) {
508
- if (ctx.ignoreActive && oldNode === document.activeElement) {
509
- return null;
510
538
  }
511
- if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) {
512
- return oldNode;
513
- }
514
- if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) {} else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
515
- handleHeadElement(oldNode, newContent, ctx);
516
- } else {
517
- morphAttributes(oldNode, newContent, ctx);
518
- if (!ignoreValueOfActiveElement(oldNode, ctx)) {
519
- morphChildren(ctx, oldNode, newContent);
539
+ return morphChildren2;
540
+ }();
541
+ const morphNode = function() {
542
+ function morphNode2(oldNode, newContent, ctx) {
543
+ if (ctx.ignoreActive && oldNode === document.activeElement) {
544
+ return null;
520
545
  }
521
- }
522
- ctx.callbacks.afterNodeMorphed(oldNode, newContent);
523
- return oldNode;
524
- }
525
- function morphAttributes(oldNode, newNode, ctx) {
526
- let type = newNode.nodeType;
527
- if (type === 1) {
528
- const oldElt = oldNode;
529
- const newElt = newNode;
530
- const oldAttributes = oldElt.attributes;
531
- const newAttributes = newElt.attributes;
532
- for (const newAttribute of newAttributes) {
533
- if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) {
534
- continue;
535
- }
536
- if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) {
537
- oldElt.setAttribute(newAttribute.name, newAttribute.value);
546
+ if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) {
547
+ return oldNode;
548
+ }
549
+ if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) {} else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
550
+ handleHeadElement(oldNode, newContent, ctx);
551
+ } else {
552
+ morphAttributes(oldNode, newContent, ctx);
553
+ if (!ignoreValueOfActiveElement(oldNode, ctx)) {
554
+ morphChildren(ctx, oldNode, newContent);
538
555
  }
539
556
  }
540
- for (let i = oldAttributes.length - 1;0 <= i; i--) {
541
- const oldAttribute = oldAttributes[i];
542
- if (!oldAttribute)
543
- continue;
544
- if (!newElt.hasAttribute(oldAttribute.name)) {
545
- if (ignoreAttribute(oldAttribute.name, oldElt, "remove", ctx)) {
557
+ ctx.callbacks.afterNodeMorphed(oldNode, newContent);
558
+ return oldNode;
559
+ }
560
+ function morphAttributes(oldNode, newNode, ctx) {
561
+ let type = newNode.nodeType;
562
+ if (type === 1) {
563
+ const oldElt = oldNode;
564
+ const newElt = newNode;
565
+ const oldAttributes = oldElt.attributes;
566
+ const newAttributes = newElt.attributes;
567
+ for (const newAttribute of newAttributes) {
568
+ if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) {
546
569
  continue;
547
570
  }
548
- oldElt.removeAttribute(oldAttribute.name);
571
+ if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) {
572
+ oldElt.setAttribute(newAttribute.name, newAttribute.value);
573
+ }
574
+ }
575
+ for (let i = oldAttributes.length - 1;0 <= i; i--) {
576
+ const oldAttribute = oldAttributes[i];
577
+ if (!oldAttribute)
578
+ continue;
579
+ if (!newElt.hasAttribute(oldAttribute.name)) {
580
+ if (ignoreAttribute(oldAttribute.name, oldElt, "remove", ctx)) {
581
+ continue;
582
+ }
583
+ oldElt.removeAttribute(oldAttribute.name);
584
+ }
585
+ }
586
+ if (!ignoreValueOfActiveElement(oldElt, ctx)) {
587
+ syncInputValue(oldElt, newElt, ctx);
549
588
  }
550
589
  }
551
- if (!ignoreValueOfActiveElement(oldElt, ctx)) {
552
- syncInputValue(oldElt, newElt, ctx);
553
- }
554
- }
555
- if (type === 8 || type === 3) {
556
- if (oldNode.nodeValue !== newNode.nodeValue) {
557
- oldNode.nodeValue = newNode.nodeValue;
590
+ if (type === 8 || type === 3) {
591
+ if (oldNode.nodeValue !== newNode.nodeValue) {
592
+ oldNode.nodeValue = newNode.nodeValue;
593
+ }
558
594
  }
559
595
  }
560
- }
561
- function syncInputValue(oldElement, newElement, ctx) {
562
- if (oldElement instanceof HTMLInputElement && newElement instanceof HTMLInputElement && newElement.type !== "file") {
563
- let newValue = newElement.value;
564
- let oldValue = oldElement.value;
565
- syncBooleanAttribute(oldElement, newElement, "checked", ctx);
566
- syncBooleanAttribute(oldElement, newElement, "disabled", ctx);
567
- if (!newElement.hasAttribute("value")) {
568
- if (!ignoreAttribute("value", oldElement, "remove", ctx)) {
569
- oldElement.value = "";
570
- oldElement.removeAttribute("value");
596
+ function syncInputValue(oldElement, newElement, ctx) {
597
+ if (oldElement instanceof HTMLInputElement && newElement instanceof HTMLInputElement && newElement.type !== "file") {
598
+ let newValue = newElement.value;
599
+ let oldValue = oldElement.value;
600
+ syncBooleanAttribute(oldElement, newElement, "checked", ctx);
601
+ syncBooleanAttribute(oldElement, newElement, "disabled", ctx);
602
+ if (!newElement.hasAttribute("value")) {
603
+ if (!ignoreAttribute("value", oldElement, "remove", ctx)) {
604
+ oldElement.value = "";
605
+ oldElement.removeAttribute("value");
606
+ }
607
+ } else if (oldValue !== newValue) {
608
+ if (!ignoreAttribute("value", oldElement, "update", ctx)) {
609
+ oldElement.setAttribute("value", newValue);
610
+ oldElement.value = newValue;
611
+ }
571
612
  }
572
- } else if (oldValue !== newValue) {
573
- if (!ignoreAttribute("value", oldElement, "update", ctx)) {
574
- oldElement.setAttribute("value", newValue);
613
+ } else if (oldElement instanceof HTMLOptionElement && newElement instanceof HTMLOptionElement) {
614
+ syncBooleanAttribute(oldElement, newElement, "selected", ctx);
615
+ } else if (oldElement instanceof HTMLTextAreaElement && newElement instanceof HTMLTextAreaElement) {
616
+ let newValue = newElement.value;
617
+ let oldValue = oldElement.value;
618
+ if (ignoreAttribute("value", oldElement, "update", ctx)) {
619
+ return;
620
+ }
621
+ if (newValue !== oldValue) {
575
622
  oldElement.value = newValue;
576
623
  }
577
- }
578
- } else if (oldElement instanceof HTMLOptionElement && newElement instanceof HTMLOptionElement) {
579
- syncBooleanAttribute(oldElement, newElement, "selected", ctx);
580
- } else if (oldElement instanceof HTMLTextAreaElement && newElement instanceof HTMLTextAreaElement) {
581
- let newValue = newElement.value;
582
- let oldValue = oldElement.value;
583
- if (ignoreAttribute("value", oldElement, "update", ctx)) {
584
- return;
585
- }
586
- if (newValue !== oldValue) {
587
- oldElement.value = newValue;
588
- }
589
- if (oldElement.firstChild && oldElement.firstChild.nodeValue !== newValue) {
590
- oldElement.firstChild.nodeValue = newValue;
624
+ if (oldElement.firstChild && oldElement.firstChild.nodeValue !== newValue) {
625
+ oldElement.firstChild.nodeValue = newValue;
626
+ }
591
627
  }
592
628
  }
593
- }
594
- function syncBooleanAttribute(oldElement, newElement, attributeName, ctx) {
595
- const newLiveValue = newElement[attributeName], oldLiveValue = oldElement[attributeName];
596
- if (newLiveValue !== oldLiveValue) {
597
- const ignoreUpdate = ignoreAttribute(attributeName, oldElement, "update", ctx);
598
- if (!ignoreUpdate) {
599
- oldElement[attributeName] = newElement[attributeName];
600
- }
601
- if (newLiveValue) {
629
+ function syncBooleanAttribute(oldElement, newElement, attributeName, ctx) {
630
+ const newLiveValue = newElement[attributeName], oldLiveValue = oldElement[attributeName];
631
+ if (newLiveValue !== oldLiveValue) {
632
+ const ignoreUpdate = ignoreAttribute(attributeName, oldElement, "update", ctx);
602
633
  if (!ignoreUpdate) {
603
- oldElement.setAttribute(attributeName, "");
634
+ oldElement[attributeName] = newElement[attributeName];
604
635
  }
605
- } else {
606
- if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) {
607
- oldElement.removeAttribute(attributeName);
636
+ if (newLiveValue) {
637
+ if (!ignoreUpdate) {
638
+ oldElement.setAttribute(attributeName, "");
639
+ }
640
+ } else {
641
+ if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) {
642
+ oldElement.removeAttribute(attributeName);
643
+ }
608
644
  }
609
645
  }
610
646
  }
611
- }
612
- function ignoreAttribute(attr, element, updateType, ctx) {
613
- if (attr === "value" && ctx.ignoreActiveValue && element === document.activeElement) {
614
- return true;
647
+ function ignoreAttribute(attr, element, updateType, ctx) {
648
+ if (attr === "value" && ctx.ignoreActiveValue && element === document.activeElement) {
649
+ return true;
650
+ }
651
+ return ctx.callbacks.beforeAttributeUpdated(attr, element, updateType) === false;
615
652
  }
616
- return ctx.callbacks.beforeAttributeUpdated(attr, element, updateType) === false;
617
- }
618
- function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
619
- return !!ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
620
- }
621
- return morphNode2;
622
- }();
623
- function withHeadBlocking(ctx, oldNode, newNode, callback) {
624
- if (ctx.head.block) {
625
- const oldHead = oldNode.querySelector("head");
626
- const newHead = newNode.querySelector("head");
627
- if (oldHead && newHead) {
628
- const promises = handleHeadElement(oldHead, newHead, ctx);
629
- return Promise.all(promises).then(() => {
630
- const newCtx = Object.assign(ctx, {
631
- head: {
632
- block: false,
633
- ignore: true
634
- }
635
- });
636
- return callback(newCtx);
637
- });
653
+ function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
654
+ return !!ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
638
655
  }
639
- }
640
- return callback(ctx);
641
- }
642
- function handleHeadElement(oldHead, newHead, ctx) {
643
- let added = [];
644
- let removed = [];
645
- let preserved = [];
646
- let nodesToAppend = [];
647
- let srcToNewHeadNodes = new Map;
648
- for (const newHeadChild of newHead.children) {
649
- srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
650
- }
651
- for (const currentHeadElt of oldHead.children) {
652
- let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
653
- let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
654
- let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
655
- if (inNewContent || isPreserved) {
656
- if (isReAppended) {
657
- removed.push(currentHeadElt);
658
- } else {
659
- srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
660
- preserved.push(currentHeadElt);
656
+ return morphNode2;
657
+ }();
658
+ function withHeadBlocking(ctx, oldNode, newNode, callback) {
659
+ if (ctx.head.block) {
660
+ const oldHead = oldNode.querySelector("head");
661
+ const newHead = newNode.querySelector("head");
662
+ if (oldHead && newHead) {
663
+ const promises = handleHeadElement(oldHead, newHead, ctx);
664
+ return Promise.all(promises).then(() => {
665
+ const newCtx = Object.assign(ctx, {
666
+ head: {
667
+ block: false,
668
+ ignore: true
669
+ }
670
+ });
671
+ return callback(newCtx);
672
+ });
661
673
  }
662
- } else {
663
- if (ctx.head.style === "append") {
674
+ }
675
+ return callback(ctx);
676
+ }
677
+ function handleHeadElement(oldHead, newHead, ctx) {
678
+ let added = [];
679
+ let removed = [];
680
+ let preserved = [];
681
+ let nodesToAppend = [];
682
+ let srcToNewHeadNodes = new Map;
683
+ for (const newHeadChild of newHead.children) {
684
+ srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
685
+ }
686
+ for (const currentHeadElt of oldHead.children) {
687
+ let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
688
+ let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
689
+ let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
690
+ if (inNewContent || isPreserved) {
664
691
  if (isReAppended) {
665
692
  removed.push(currentHeadElt);
666
- nodesToAppend.push(currentHeadElt);
693
+ } else {
694
+ srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
695
+ preserved.push(currentHeadElt);
667
696
  }
668
697
  } else {
669
- if (ctx.head.shouldRemove(currentHeadElt) !== false) {
670
- removed.push(currentHeadElt);
698
+ if (ctx.head.style === "append") {
699
+ if (isReAppended) {
700
+ removed.push(currentHeadElt);
701
+ nodesToAppend.push(currentHeadElt);
702
+ }
703
+ } else {
704
+ if (ctx.head.shouldRemove(currentHeadElt) !== false) {
705
+ removed.push(currentHeadElt);
706
+ }
671
707
  }
672
708
  }
673
709
  }
674
- }
675
- nodesToAppend.push(...srcToNewHeadNodes.values());
676
- let promises = [];
677
- for (const newNode of nodesToAppend) {
678
- let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild;
679
- if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
680
- if ("href" in newElt && newElt.href || "src" in newElt && newElt.src) {
681
- let resolve;
682
- let promise = new Promise(function(_resolve) {
683
- resolve = _resolve;
684
- });
685
- newElt.addEventListener("load", function() {
686
- resolve();
687
- });
688
- promises.push(promise);
710
+ nodesToAppend.push(...srcToNewHeadNodes.values());
711
+ let promises = [];
712
+ for (const newNode of nodesToAppend) {
713
+ let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild;
714
+ if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
715
+ if ("href" in newElt && newElt.href || "src" in newElt && newElt.src) {
716
+ let resolve;
717
+ let promise = new Promise(function(_resolve) {
718
+ resolve = _resolve;
719
+ });
720
+ newElt.addEventListener("load", function() {
721
+ resolve();
722
+ });
723
+ promises.push(promise);
724
+ }
725
+ oldHead.appendChild(newElt);
726
+ ctx.callbacks.afterNodeAdded(newElt);
727
+ added.push(newElt);
689
728
  }
690
- oldHead.appendChild(newElt);
691
- ctx.callbacks.afterNodeAdded(newElt);
692
- added.push(newElt);
693
729
  }
694
- }
695
- for (const removedElement of removed) {
696
- if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
697
- oldHead.removeChild(removedElement);
698
- ctx.callbacks.afterNodeRemoved(removedElement);
699
- }
700
- }
701
- ctx.head.afterHeadMorphed(oldHead, {
702
- added,
703
- kept: preserved,
704
- removed
705
- });
706
- return promises;
707
- }
708
- const createMorphContext = function() {
709
- function createMorphContext2(oldNode, newContent, config) {
710
- const { persistentIds, idMap } = createIdMaps(oldNode, newContent);
711
- const mergedConfig = mergeDefaults(config);
712
- const morphStyle = mergedConfig.morphStyle || "outerHTML";
713
- if (!["innerHTML", "outerHTML"].includes(morphStyle)) {
714
- throw `Do not understand how to morph style ${morphStyle}`;
715
- }
716
- return {
717
- target: oldNode,
718
- newContent,
719
- config: mergedConfig,
720
- morphStyle,
721
- ignoreActive: mergedConfig.ignoreActive,
722
- ignoreActiveValue: mergedConfig.ignoreActiveValue,
723
- restoreFocus: mergedConfig.restoreFocus,
724
- idMap,
725
- persistentIds,
726
- pantry: createPantry(),
727
- activeElementAndParents: createActiveElementAndParents(oldNode),
728
- callbacks: mergedConfig.callbacks,
729
- head: mergedConfig.head
730
- };
731
- }
732
- function mergeDefaults(config) {
733
- let finalConfig = Object.assign({}, defaults);
734
- Object.assign(finalConfig, config);
735
- finalConfig.callbacks = Object.assign({}, defaults.callbacks, config.callbacks);
736
- finalConfig.head = Object.assign({}, defaults.head, config.head);
737
- return finalConfig;
738
- }
739
- function createPantry() {
740
- const pantry = document.createElement("div");
741
- pantry.hidden = true;
742
- document.body.insertAdjacentElement("afterend", pantry);
743
- return pantry;
744
- }
745
- function createActiveElementAndParents(oldNode) {
746
- let activeElementAndParents = [];
747
- let elt = document.activeElement;
748
- if (elt?.tagName !== "BODY" && oldNode.contains(elt)) {
749
- while (elt) {
750
- activeElementAndParents.push(elt);
751
- if (elt === oldNode)
752
- break;
753
- elt = elt.parentElement;
730
+ for (const removedElement of removed) {
731
+ if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
732
+ oldHead.removeChild(removedElement);
733
+ ctx.callbacks.afterNodeRemoved(removedElement);
754
734
  }
755
735
  }
756
- return activeElementAndParents;
757
- }
758
- function findIdElements(root) {
759
- let elements = Array.from(root.querySelectorAll("[id]"));
760
- if (root.getAttribute?.("id")) {
761
- elements.push(root);
762
- }
763
- return elements;
764
- }
765
- function populateIdMapWithTree(idMap, persistentIds, root, elements) {
766
- for (const elt of elements) {
767
- const id = elt.getAttribute("id");
768
- if (persistentIds.has(id)) {
769
- let current = elt;
770
- while (current) {
771
- let idSet = idMap.get(current);
772
- if (idSet == null) {
773
- idSet = new Set;
774
- idMap.set(current, idSet);
775
- }
776
- idSet.add(id);
777
- if (current === root)
736
+ ctx.head.afterHeadMorphed(oldHead, {
737
+ added,
738
+ kept: preserved,
739
+ removed
740
+ });
741
+ return promises;
742
+ }
743
+ const createMorphContext = function() {
744
+ function createMorphContext2(oldNode, newContent, config) {
745
+ const { persistentIds, idMap } = createIdMaps(oldNode, newContent);
746
+ const mergedConfig = mergeDefaults(config);
747
+ const morphStyle = mergedConfig.morphStyle || "outerHTML";
748
+ if (!["innerHTML", "outerHTML"].includes(morphStyle)) {
749
+ throw `Do not understand how to morph style ${morphStyle}`;
750
+ }
751
+ return {
752
+ target: oldNode,
753
+ newContent,
754
+ config: mergedConfig,
755
+ morphStyle,
756
+ ignoreActive: mergedConfig.ignoreActive,
757
+ ignoreActiveValue: mergedConfig.ignoreActiveValue,
758
+ restoreFocus: mergedConfig.restoreFocus,
759
+ idMap,
760
+ persistentIds,
761
+ pantry: createPantry(),
762
+ activeElementAndParents: createActiveElementAndParents(oldNode),
763
+ callbacks: mergedConfig.callbacks,
764
+ head: mergedConfig.head
765
+ };
766
+ }
767
+ function mergeDefaults(config) {
768
+ let finalConfig = Object.assign({}, defaults);
769
+ Object.assign(finalConfig, config);
770
+ finalConfig.callbacks = Object.assign({}, defaults.callbacks, config.callbacks);
771
+ finalConfig.head = Object.assign({}, defaults.head, config.head);
772
+ return finalConfig;
773
+ }
774
+ function createPantry() {
775
+ const pantry = document.createElement("div");
776
+ pantry.hidden = true;
777
+ document.body.insertAdjacentElement("afterend", pantry);
778
+ return pantry;
779
+ }
780
+ function createActiveElementAndParents(oldNode) {
781
+ let activeElementAndParents = [];
782
+ let elt = document.activeElement;
783
+ if (elt?.tagName !== "BODY" && oldNode.contains(elt)) {
784
+ while (elt) {
785
+ activeElementAndParents.push(elt);
786
+ if (elt === oldNode)
778
787
  break;
779
- current = current.parentElement;
788
+ elt = elt.parentElement;
780
789
  }
781
790
  }
791
+ return activeElementAndParents;
782
792
  }
783
- }
784
- function createIdMaps(oldContent, newContent) {
785
- const oldIdElements = findIdElements(oldContent);
786
- const newIdElements = findIdElements(newContent);
787
- const persistentIds = createPersistentIds(oldIdElements, newIdElements);
788
- let idMap = new Map;
789
- populateIdMapWithTree(idMap, persistentIds, oldContent, oldIdElements);
790
- const newRoot = newContent.__idiomorphRoot || newContent;
791
- populateIdMapWithTree(idMap, persistentIds, newRoot, newIdElements);
792
- return { persistentIds, idMap };
793
- }
794
- function createPersistentIds(oldIdElements, newIdElements) {
795
- let duplicateIds = new Set;
796
- let oldIdTagNameMap = new Map;
797
- for (const { id, tagName } of oldIdElements) {
798
- if (oldIdTagNameMap.has(id)) {
799
- duplicateIds.add(id);
800
- } else {
801
- oldIdTagNameMap.set(id, tagName);
793
+ function findIdElements(root) {
794
+ let elements = Array.from(root.querySelectorAll("[id]"));
795
+ if (root.getAttribute?.("id")) {
796
+ elements.push(root);
802
797
  }
803
- }
804
- let persistentIds = new Set;
805
- for (const { id, tagName } of newIdElements) {
806
- if (persistentIds.has(id)) {
807
- duplicateIds.add(id);
808
- } else if (oldIdTagNameMap.get(id) === tagName) {
809
- persistentIds.add(id);
798
+ return elements;
799
+ }
800
+ function populateIdMapWithTree(idMap, persistentIds, root, elements) {
801
+ for (const elt of elements) {
802
+ const id = elt.getAttribute("id");
803
+ if (persistentIds.has(id)) {
804
+ let current = elt;
805
+ while (current) {
806
+ let idSet = idMap.get(current);
807
+ if (idSet == null) {
808
+ idSet = new Set;
809
+ idMap.set(current, idSet);
810
+ }
811
+ idSet.add(id);
812
+ if (current === root)
813
+ break;
814
+ current = current.parentElement;
815
+ }
816
+ }
810
817
  }
811
818
  }
812
- for (const id of duplicateIds) {
813
- persistentIds.delete(id);
819
+ function createIdMaps(oldContent, newContent) {
820
+ const oldIdElements = findIdElements(oldContent);
821
+ const newIdElements = findIdElements(newContent);
822
+ const persistentIds = createPersistentIds(oldIdElements, newIdElements);
823
+ let idMap = new Map;
824
+ populateIdMapWithTree(idMap, persistentIds, oldContent, oldIdElements);
825
+ const newRoot = newContent.__idiomorphRoot || newContent;
826
+ populateIdMapWithTree(idMap, persistentIds, newRoot, newIdElements);
827
+ return { persistentIds, idMap };
828
+ }
829
+ function createPersistentIds(oldIdElements, newIdElements) {
830
+ let duplicateIds = new Set;
831
+ let oldIdTagNameMap = new Map;
832
+ for (const { id, tagName } of oldIdElements) {
833
+ if (oldIdTagNameMap.has(id)) {
834
+ duplicateIds.add(id);
835
+ } else {
836
+ oldIdTagNameMap.set(id, tagName);
837
+ }
838
+ }
839
+ let persistentIds = new Set;
840
+ for (const { id, tagName } of newIdElements) {
841
+ if (persistentIds.has(id)) {
842
+ duplicateIds.add(id);
843
+ } else if (oldIdTagNameMap.get(id) === tagName) {
844
+ persistentIds.add(id);
845
+ }
846
+ }
847
+ for (const id of duplicateIds) {
848
+ persistentIds.delete(id);
849
+ }
850
+ return persistentIds;
814
851
  }
815
- return persistentIds;
816
- }
817
- return createMorphContext2;
818
- }();
819
- const { normalizeElement, normalizeParent } = function() {
820
- const generatedByIdiomorph = new WeakSet;
821
- function normalizeElement2(content) {
822
- if (content instanceof Document) {
823
- return content.documentElement;
824
- } else {
825
- return content;
852
+ return createMorphContext2;
853
+ }();
854
+ const { normalizeElement, normalizeParent } = function() {
855
+ const generatedByIdiomorph = new WeakSet;
856
+ function normalizeElement2(content) {
857
+ if (content instanceof Document) {
858
+ return content.documentElement;
859
+ } else {
860
+ return content;
861
+ }
826
862
  }
827
- }
828
- function normalizeParent2(newContent) {
829
- if (newContent == null) {
830
- return document.createElement("div");
831
- } else if (typeof newContent === "string") {
832
- return normalizeParent2(parseContent(newContent));
833
- } else if (generatedByIdiomorph.has(newContent)) {
834
- return newContent;
835
- } else if (newContent instanceof Node) {
836
- if (newContent.parentNode) {
837
- return new SlicedParentNode(newContent);
863
+ function normalizeParent2(newContent) {
864
+ if (newContent == null) {
865
+ return document.createElement("div");
866
+ } else if (typeof newContent === "string") {
867
+ return normalizeParent2(parseContent(newContent));
868
+ } else if (generatedByIdiomorph.has(newContent)) {
869
+ return newContent;
870
+ } else if (newContent instanceof Node) {
871
+ if (newContent.parentNode) {
872
+ return new SlicedParentNode(newContent);
873
+ } else {
874
+ const dummyParent = document.createElement("div");
875
+ dummyParent.append(newContent);
876
+ return dummyParent;
877
+ }
838
878
  } else {
839
879
  const dummyParent = document.createElement("div");
840
- dummyParent.append(newContent);
880
+ for (const elt of [...newContent]) {
881
+ dummyParent.append(elt);
882
+ }
841
883
  return dummyParent;
842
884
  }
843
- } else {
844
- const dummyParent = document.createElement("div");
845
- for (const elt of [...newContent]) {
846
- dummyParent.append(elt);
847
- }
848
- return dummyParent;
849
885
  }
850
- }
851
886
 
852
- class SlicedParentNode {
853
- constructor(node) {
854
- this.originalNode = node;
855
- this.realParentNode = node.parentNode;
856
- this.previousSibling = node.previousSibling;
857
- this.nextSibling = node.nextSibling;
858
- }
859
- get childNodes() {
860
- const nodes = [];
861
- let cursor = this.previousSibling ? this.previousSibling.nextSibling : this.realParentNode.firstChild;
862
- while (cursor && cursor != this.nextSibling) {
863
- nodes.push(cursor);
864
- cursor = cursor.nextSibling;
887
+ class SlicedParentNode {
888
+ constructor(node) {
889
+ this.originalNode = node;
890
+ this.realParentNode = node.parentNode;
891
+ this.previousSibling = node.previousSibling;
892
+ this.nextSibling = node.nextSibling;
893
+ }
894
+ get childNodes() {
895
+ const nodes = [];
896
+ let cursor = this.previousSibling ? this.previousSibling.nextSibling : this.realParentNode.firstChild;
897
+ while (cursor && cursor != this.nextSibling) {
898
+ nodes.push(cursor);
899
+ cursor = cursor.nextSibling;
900
+ }
901
+ return nodes;
902
+ }
903
+ querySelectorAll(selector) {
904
+ return this.childNodes.reduce((results, node) => {
905
+ if (node instanceof Element) {
906
+ if (node.matches(selector))
907
+ results.push(node);
908
+ const nodeList = node.querySelectorAll(selector);
909
+ for (let i = 0;i < nodeList.length; i++) {
910
+ results.push(nodeList[i]);
911
+ }
912
+ }
913
+ return results;
914
+ }, []);
915
+ }
916
+ insertBefore(node, referenceNode) {
917
+ return this.realParentNode.insertBefore(node, referenceNode);
918
+ }
919
+ moveBefore(node, referenceNode) {
920
+ return this.realParentNode.moveBefore(node, referenceNode);
921
+ }
922
+ get __idiomorphRoot() {
923
+ return this.originalNode;
865
924
  }
866
- return nodes;
867
925
  }
868
- querySelectorAll(selector) {
869
- return this.childNodes.reduce((results, node) => {
870
- if (node instanceof Element) {
871
- if (node.matches(selector))
872
- results.push(node);
873
- const nodeList = node.querySelectorAll(selector);
874
- for (let i = 0;i < nodeList.length; i++) {
875
- results.push(nodeList[i]);
926
+ function parseContent(newContent) {
927
+ let parser = new DOMParser;
928
+ let contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, "");
929
+ if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
930
+ let content = parser.parseFromString(newContent, "text/html");
931
+ if (contentWithSvgsRemoved.match(/<\/html>/)) {
932
+ generatedByIdiomorph.add(content);
933
+ return content;
934
+ } else {
935
+ let htmlElement = content.firstChild;
936
+ if (htmlElement) {
937
+ generatedByIdiomorph.add(htmlElement);
876
938
  }
939
+ return htmlElement;
877
940
  }
878
- return results;
879
- }, []);
880
- }
881
- insertBefore(node, referenceNode) {
882
- return this.realParentNode.insertBefore(node, referenceNode);
883
- }
884
- moveBefore(node, referenceNode) {
885
- return this.realParentNode.moveBefore(node, referenceNode);
886
- }
887
- get __idiomorphRoot() {
888
- return this.originalNode;
889
- }
890
- }
891
- function parseContent(newContent) {
892
- let parser = new DOMParser;
893
- let contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, "");
894
- if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
895
- let content = parser.parseFromString(newContent, "text/html");
896
- if (contentWithSvgsRemoved.match(/<\/html>/)) {
941
+ } else {
942
+ let responseDoc = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html");
943
+ let content = responseDoc.body.querySelector("template").content;
897
944
  generatedByIdiomorph.add(content);
898
945
  return content;
899
- } else {
900
- let htmlElement = content.firstChild;
901
- if (htmlElement) {
902
- generatedByIdiomorph.add(htmlElement);
903
- }
904
- return htmlElement;
905
946
  }
906
- } else {
907
- let responseDoc = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html");
908
- let content = responseDoc.body.querySelector("template").content;
909
- generatedByIdiomorph.add(content);
910
- return content;
911
947
  }
912
- }
913
- return { normalizeElement: normalizeElement2, normalizeParent: normalizeParent2 };
948
+ return { normalizeElement: normalizeElement2, normalizeParent: normalizeParent2 };
949
+ }();
950
+ return {
951
+ morph,
952
+ defaults
953
+ };
914
954
  }();
915
- return {
916
- morph,
917
- defaults
918
- };
919
- }();
920
955
 
921
- // src/utils.js
922
- function splitUrl(url) {
923
- let hash = "";
924
- let params = "";
925
- let index = url.indexOf("#");
926
- if (index >= 0) {
927
- hash = url.slice(index);
928
- url = url.slice(0, index);
929
- }
930
- const comboSign = url.indexOf("??");
931
- if (comboSign >= 0) {
932
- if (comboSign + 1 !== url.lastIndexOf("?")) {
933
- index = url.lastIndexOf("?");
956
+ // src/utils.js
957
+ function splitUrl(url) {
958
+ let hash = "";
959
+ let params = "";
960
+ let index = url.indexOf("#");
961
+ if (index >= 0) {
962
+ hash = url.slice(index);
963
+ url = url.slice(0, index);
964
+ }
965
+ const comboSign = url.indexOf("??");
966
+ if (comboSign >= 0) {
967
+ if (comboSign + 1 !== url.lastIndexOf("?")) {
968
+ index = url.lastIndexOf("?");
969
+ }
970
+ } else {
971
+ index = url.indexOf("?");
934
972
  }
935
- } else {
936
- index = url.indexOf("?");
937
- }
938
- if (index >= 0) {
939
- params = url.slice(index);
940
- url = url.slice(0, index);
941
- }
942
- return { url, params, hash };
943
- }
944
- function pathFromUrl(url) {
945
- if (!url) {
946
- return "";
947
- }
948
- let path;
949
- ({ url } = splitUrl(url));
950
- if (url.indexOf("file://") === 0) {
951
- path = url.replace(new RegExp("^file://(localhost)?"), "");
952
- } else {
953
- path = url.replace(new RegExp("^([^:]+:)?//([^:/]+)(:\\d*)?/"), "/");
954
- }
955
- return decodeURIComponent(path);
956
- }
957
- function numberOfMatchingSegments(left, right) {
958
- left = left.replace(/^\/+/, "").toLowerCase();
959
- right = right.replace(/^\/+/, "").toLowerCase();
960
- if (left === right) {
961
- return 1e4;
962
- }
963
- const comps1 = left.split(/\/|\\/).reverse();
964
- const comps2 = right.split(/\/|\\/).reverse();
965
- const len = Math.min(comps1.length, comps2.length);
966
- let eqCount = 0;
967
- while (eqCount < len && comps1[eqCount] === comps2[eqCount]) {
968
- ++eqCount;
969
- }
970
- return eqCount;
971
- }
972
- function pickBestMatch(path, objects, pathFunc = (s) => s) {
973
- let bestMatch = { score: 0 };
974
- for (const object of objects) {
975
- const score = numberOfMatchingSegments(path, pathFunc(object));
976
- if (score > bestMatch.score) {
977
- bestMatch = { object, score };
973
+ if (index >= 0) {
974
+ params = url.slice(index);
975
+ url = url.slice(0, index);
978
976
  }
977
+ return { url, params, hash };
979
978
  }
980
- if (bestMatch.score === 0) {
981
- return null;
982
- }
983
- return bestMatch;
984
- }
985
- function generateCacheBustUrl(url) {
986
- const { url: cleanUrl, params, hash } = splitUrl(url);
987
- const expando = `livereload=${Date.now()}`;
988
- if (!params) {
989
- return `${cleanUrl}?${expando}${hash}`;
979
+ function pathFromUrl(url) {
980
+ if (!url) {
981
+ return "";
982
+ }
983
+ let path;
984
+ ({ url } = splitUrl(url));
985
+ if (url.indexOf("file://") === 0) {
986
+ path = url.replace(new RegExp("^file://(localhost)?"), "");
987
+ } else {
988
+ path = url.replace(new RegExp("^([^:]+:)?//([^:/]+)(:\\d*)?/"), "/");
989
+ }
990
+ return decodeURIComponent(path);
990
991
  }
991
- if (params.includes("livereload=")) {
992
- const newParams = params.replace(/([?&])livereload=\d+/, `$1${expando}`);
993
- return `${cleanUrl}${newParams}${hash}`;
992
+ function numberOfMatchingSegments(left, right) {
993
+ left = left.replace(/^\/+/, "").toLowerCase();
994
+ right = right.replace(/^\/+/, "").toLowerCase();
995
+ if (left === right) {
996
+ return 1e4;
997
+ }
998
+ const comps1 = left.split(/\/|\\/).reverse();
999
+ const comps2 = right.split(/\/|\\/).reverse();
1000
+ const len = Math.min(comps1.length, comps2.length);
1001
+ let eqCount = 0;
1002
+ while (eqCount < len && comps1[eqCount] === comps2[eqCount]) {
1003
+ ++eqCount;
1004
+ }
1005
+ return eqCount;
994
1006
  }
995
- return `${cleanUrl}${params}&${expando}${hash}`;
996
- }
997
- function waitForStylesheetLoad(linkElement, timeout = 15000) {
998
- return new Promise((resolve) => {
999
- let resolved = false;
1000
- const finish = () => {
1001
- if (resolved)
1002
- return;
1003
- resolved = true;
1004
- resolve();
1005
- };
1006
- linkElement.onload = () => {
1007
- finish();
1008
- };
1009
- const pollInterval = 50;
1010
- const poll = () => {
1011
- if (resolved)
1012
- return;
1013
- if (linkElement.sheet) {
1014
- finish();
1015
- return;
1007
+ function pickBestMatch(path, objects, pathFunc = (s) => s) {
1008
+ let bestMatch = { score: 0 };
1009
+ for (const object of objects) {
1010
+ const score = numberOfMatchingSegments(path, pathFunc(object));
1011
+ if (score > bestMatch.score) {
1012
+ bestMatch = { object, score };
1016
1013
  }
1017
- setTimeout(poll, pollInterval);
1018
- };
1019
- setTimeout(poll, pollInterval);
1020
- setTimeout(finish, timeout);
1021
- });
1022
- }
1023
-
1024
- // src/morpher.js
1025
- class Morpher {
1026
- constructor(window2, console2, Timer2, importCacheWaitPeriod = 200) {
1027
- this.window = window2;
1028
- this.console = console2;
1029
- this.Timer = Timer2;
1030
- this.document = window2.document;
1031
- this.importCacheWaitPeriod = importCacheWaitPeriod;
1032
- }
1033
- reload(path, options = {}) {
1034
- const isCSSFile = path.match(/\.css(?:\.map)?$/i);
1035
- const isImageFile = path.match(/\.(jpe?g|png|gif|svg|webp|ico)$/i);
1036
- const isJSFile = path.match(/\.m?js$/i);
1037
- if (isCSSFile && options.liveCSS) {
1038
- return this.reloadStylesheet(path, options);
1039
1014
  }
1040
- if (isImageFile && options.liveImg) {
1041
- return this.reloadImages(path);
1015
+ if (bestMatch.score === 0) {
1016
+ return null;
1042
1017
  }
1043
- if (isJSFile) {
1044
- return this.reloadPage();
1018
+ return bestMatch;
1019
+ }
1020
+ function generateCacheBustUrl(url) {
1021
+ const { url: cleanUrl, params, hash } = splitUrl(url);
1022
+ const expando = `livereload=${Date.now()}`;
1023
+ if (!params) {
1024
+ return `${cleanUrl}?${expando}${hash}`;
1045
1025
  }
1046
- if (options.morphHTML) {
1047
- return this.morphHTML(path, options);
1026
+ if (params.includes("livereload=")) {
1027
+ const newParams = params.replace(/([?&])livereload=\d+/, `$1${expando}`);
1028
+ return `${cleanUrl}${newParams}${hash}`;
1048
1029
  }
1049
- this.reloadPage();
1030
+ return `${cleanUrl}${params}&${expando}${hash}`;
1050
1031
  }
1051
- async morphHTML(path, options = {}) {
1052
- try {
1053
- const response = await fetch(this.window.location.href, {
1054
- cache: "no-cache",
1055
- headers: { "X-Live-Morph": "true" }
1056
- });
1057
- if (!response.ok) {
1058
- throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);
1059
- }
1060
- let html = await response.text();
1061
- html = html.replace(/<!DOCTYPE[^>]*>/i, "").trim();
1062
- Idiomorph.morph(this.document.documentElement, html, {
1063
- head: {
1064
- style: "merge",
1065
- shouldPreserve: (elt) => {
1066
- if (elt.tagName === "SCRIPT" && elt.src) {
1067
- return elt.src.toLowerCase().includes("livereload-morph");
1032
+ function waitForStylesheetLoad(linkElement, timeout = 15000) {
1033
+ return new Promise((resolve) => {
1034
+ let resolved = false;
1035
+ const finish = () => {
1036
+ if (resolved)
1037
+ return;
1038
+ resolved = true;
1039
+ resolve();
1040
+ };
1041
+ linkElement.onload = () => {
1042
+ finish();
1043
+ };
1044
+ const pollInterval = 50;
1045
+ const poll = () => {
1046
+ if (resolved)
1047
+ return;
1048
+ if (linkElement.sheet) {
1049
+ finish();
1050
+ return;
1051
+ }
1052
+ setTimeout(poll, pollInterval);
1053
+ };
1054
+ setTimeout(poll, pollInterval);
1055
+ setTimeout(finish, timeout);
1056
+ });
1057
+ }
1058
+
1059
+ // src/morpher.js
1060
+ class Morpher {
1061
+ constructor(window2, console2, Timer2, importCacheWaitPeriod = 200) {
1062
+ this.window = window2;
1063
+ this.console = console2;
1064
+ this.Timer = Timer2;
1065
+ this.document = window2.document;
1066
+ this.importCacheWaitPeriod = importCacheWaitPeriod;
1067
+ }
1068
+ reload(path, options = {}) {
1069
+ const isCSSFile = path.match(/\.css(?:\.map)?$/i);
1070
+ const isImageFile = path.match(/\.(jpe?g|png|gif|svg|webp|ico)$/i);
1071
+ const isJSFile = path.match(/\.m?js$/i);
1072
+ if (isCSSFile && options.liveCSS) {
1073
+ return this.reloadStylesheet(path, options);
1074
+ }
1075
+ if (isImageFile && options.liveImg) {
1076
+ return this.reloadImages(path);
1077
+ }
1078
+ if (isJSFile) {
1079
+ return this.reloadPage();
1080
+ }
1081
+ if (options.morphHTML) {
1082
+ return this.morphHTML(path, options);
1083
+ }
1084
+ this.reloadPage();
1085
+ }
1086
+ async morphHTML(path, options = {}) {
1087
+ try {
1088
+ const response = await fetch(this.window.location.href, {
1089
+ cache: "no-cache",
1090
+ headers: { "X-Live-Morph": "true" }
1091
+ });
1092
+ if (!response.ok) {
1093
+ throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);
1094
+ }
1095
+ let html = await response.text();
1096
+ html = html.replace(/<!DOCTYPE[^>]*>/i, "").trim();
1097
+ Idiomorph.morph(this.document.documentElement, html, {
1098
+ head: {
1099
+ style: "merge",
1100
+ shouldPreserve: (elt) => {
1101
+ if (elt.tagName === "SCRIPT" && elt.src) {
1102
+ return elt.src.toLowerCase().includes("livereload-morph");
1103
+ }
1104
+ return false;
1068
1105
  }
1069
- return false;
1070
- }
1071
- },
1072
- callbacks: {
1073
- beforeAttributeUpdated: (attributeName, node, mutationType) => {
1074
- if (node.tagName === "INPUT" || node.tagName === "TEXTAREA" || node.tagName === "SELECT") {
1075
- if (attributeName === "value" || attributeName === "checked") {
1106
+ },
1107
+ callbacks: {
1108
+ beforeAttributeUpdated: (attributeName, node, mutationType) => {
1109
+ if (node.tagName === "INPUT" || node.tagName === "TEXTAREA" || node.tagName === "SELECT") {
1110
+ if (attributeName === "value" || attributeName === "checked") {
1111
+ return false;
1112
+ }
1113
+ }
1114
+ if (node.tagName === "DETAILS" && attributeName === "open") {
1076
1115
  return false;
1077
1116
  }
1117
+ return true;
1078
1118
  }
1079
- if (node.tagName === "DETAILS" && attributeName === "open") {
1080
- return false;
1081
- }
1082
- return true;
1083
1119
  }
1120
+ });
1121
+ this.console.log("HTML morphed successfully");
1122
+ } catch (error) {
1123
+ this.console.error(`Morph failed: ${error.message}`);
1124
+ if (options.fallbackToReload !== false) {
1125
+ this.console.log("Falling back to full page reload");
1126
+ this.reloadPage();
1084
1127
  }
1085
- });
1086
- this.console.log("HTML morphed successfully");
1087
- } catch (error) {
1088
- this.console.error(`Morph failed: ${error.message}`);
1089
- if (options.fallbackToReload !== false) {
1090
- this.console.log("Falling back to full page reload");
1091
- this.reloadPage();
1092
1128
  }
1093
1129
  }
1094
- }
1095
- async reloadStylesheet(path, options = {}) {
1096
- try {
1097
- const links = Array.from(this.document.getElementsByTagName("link")).filter((link) => link.rel && link.rel.match(/^stylesheet$/i) && !link.__LiveReload_pendingRemoval);
1098
- const imported = [];
1099
- for (const style of Array.from(this.document.getElementsByTagName("style"))) {
1100
- if (style.sheet) {
1101
- this.collectImportedStylesheets(style, style.sheet, imported);
1130
+ async reloadStylesheet(path, options = {}) {
1131
+ try {
1132
+ const links = Array.from(this.document.getElementsByTagName("link")).filter((link) => link.rel && link.rel.match(/^stylesheet$/i) && !link.__LiveReload_pendingRemoval);
1133
+ const imported = [];
1134
+ for (const style of Array.from(this.document.getElementsByTagName("style"))) {
1135
+ if (style.sheet) {
1136
+ this.collectImportedStylesheets(style, style.sheet, imported);
1137
+ }
1102
1138
  }
1103
- }
1104
- for (const link of links) {
1105
- if (link.sheet) {
1106
- this.collectImportedStylesheets(link, link.sheet, imported);
1139
+ for (const link of links) {
1140
+ if (link.sheet) {
1141
+ this.collectImportedStylesheets(link, link.sheet, imported);
1142
+ }
1107
1143
  }
1108
- }
1109
- if (this.window.StyleFix && this.document.querySelectorAll) {
1110
- for (const style of Array.from(this.document.querySelectorAll("style[data-href]"))) {
1111
- links.push(style);
1144
+ if (this.window.StyleFix && this.document.querySelectorAll) {
1145
+ for (const style of Array.from(this.document.querySelectorAll("style[data-href]"))) {
1146
+ links.push(style);
1147
+ }
1112
1148
  }
1113
- }
1114
- this.console.log(`CSS reload: found ${links.length} LINKed stylesheets, ${imported.length} @imported stylesheets`);
1115
- const match = pickBestMatch(path, links.concat(imported), (item) => pathFromUrl(item.href || this.linkHref(item)));
1116
- if (!match) {
1117
- if (options.reloadMissingCSS !== false) {
1118
- this.console.log(`CSS reload: no match found for '${path}', reloading all stylesheets`);
1119
- for (const link of links) {
1120
- await this.reattachStylesheetLink(link);
1149
+ this.console.log(`CSS reload: found ${links.length} LINKed stylesheets, ${imported.length} @imported stylesheets`);
1150
+ const match = pickBestMatch(path, links.concat(imported), (item) => pathFromUrl(item.href || this.linkHref(item)));
1151
+ if (!match) {
1152
+ if (options.reloadMissingCSS !== false) {
1153
+ this.console.log(`CSS reload: no match found for '${path}', reloading all stylesheets`);
1154
+ for (const link of links) {
1155
+ await this.reattachStylesheetLink(link);
1156
+ }
1157
+ } else {
1158
+ this.console.log(`CSS reload: no match found for '${path}', skipping (reloadMissingCSS=false)`);
1121
1159
  }
1160
+ return;
1161
+ }
1162
+ if (match.object.rule) {
1163
+ this.console.log(`CSS reload: reloading @imported stylesheet: ${match.object.href}`);
1164
+ await this.reattachImportedRule(match.object);
1122
1165
  } else {
1123
- this.console.log(`CSS reload: no match found for '${path}', skipping (reloadMissingCSS=false)`);
1166
+ this.console.log(`CSS reload: reloading stylesheet: ${this.linkHref(match.object)}`);
1167
+ await this.reattachStylesheetLink(match.object);
1124
1168
  }
1169
+ } catch (error) {
1170
+ this.console.error(`Stylesheet reload failed: ${error.message}`);
1171
+ this.console.error("Stack:", error.stack);
1172
+ }
1173
+ }
1174
+ async reattachStylesheetLink(link) {
1175
+ if (link.__LiveReload_pendingRemoval) {
1125
1176
  return;
1126
1177
  }
1127
- if (match.object.rule) {
1128
- this.console.log(`CSS reload: reloading @imported stylesheet: ${match.object.href}`);
1129
- await this.reattachImportedRule(match.object);
1178
+ link.__LiveReload_pendingRemoval = true;
1179
+ let clone;
1180
+ if (link.tagName === "STYLE") {
1181
+ clone = this.document.createElement("link");
1182
+ clone.rel = "stylesheet";
1183
+ clone.media = link.media;
1184
+ clone.disabled = link.disabled;
1130
1185
  } else {
1131
- this.console.log(`CSS reload: reloading stylesheet: ${this.linkHref(match.object)}`);
1132
- await this.reattachStylesheetLink(match.object);
1186
+ clone = link.cloneNode(false);
1133
1187
  }
1134
- } catch (error) {
1135
- this.console.error(`Stylesheet reload failed: ${error.message}`);
1136
- this.console.error("Stack:", error.stack);
1137
- }
1138
- }
1139
- async reattachStylesheetLink(link) {
1140
- if (link.__LiveReload_pendingRemoval) {
1141
- return;
1142
- }
1143
- link.__LiveReload_pendingRemoval = true;
1144
- let clone;
1145
- if (link.tagName === "STYLE") {
1146
- clone = this.document.createElement("link");
1147
- clone.rel = "stylesheet";
1148
- clone.media = link.media;
1149
- clone.disabled = link.disabled;
1150
- } else {
1151
- clone = link.cloneNode(false);
1152
- }
1153
- clone.href = generateCacheBustUrl(this.linkHref(link));
1154
- const parent = link.parentNode;
1155
- if (parent.lastChild === link) {
1156
- parent.appendChild(clone);
1157
- } else {
1158
- parent.insertBefore(clone, link.nextSibling);
1159
- }
1160
- await waitForStylesheetLoad(clone);
1161
- const additionalWait = /AppleWebKit/.test(this.window.navigator.userAgent) ? 5 : 200;
1162
- await new Promise((resolve) => this.Timer.start(additionalWait, resolve));
1163
- if (link.parentNode) {
1164
- link.parentNode.removeChild(link);
1165
- }
1166
- if (this.window.StyleFix) {
1167
- this.window.StyleFix.link(clone);
1168
- }
1169
- }
1170
- reloadPage() {
1171
- this.window.location.reload();
1172
- }
1173
- reloadImages(path) {
1174
- for (const img of Array.from(this.document.images)) {
1175
- if (this.pathsMatch(path, pathFromUrl(img.src))) {
1176
- img.src = generateCacheBustUrl(img.src);
1188
+ clone.href = generateCacheBustUrl(this.linkHref(link));
1189
+ const parent = link.parentNode;
1190
+ if (parent.lastChild === link) {
1191
+ parent.appendChild(clone);
1192
+ } else {
1193
+ parent.insertBefore(clone, link.nextSibling);
1177
1194
  }
1178
- }
1179
- const bgSelectors = ["background", "border"];
1180
- const bgStyleNames = ["backgroundImage", "borderImage", "webkitBorderImage", "MozBorderImage"];
1181
- for (const selector of bgSelectors) {
1182
- for (const el of Array.from(this.document.querySelectorAll(`[style*=${selector}]`))) {
1183
- this.reloadStyleImages(el.style, bgStyleNames, path);
1195
+ await waitForStylesheetLoad(clone);
1196
+ const additionalWait = /AppleWebKit/.test(this.window.navigator.userAgent) ? 5 : 200;
1197
+ await new Promise((resolve) => this.Timer.start(additionalWait, resolve));
1198
+ if (link.parentNode) {
1199
+ link.parentNode.removeChild(link);
1200
+ }
1201
+ if (this.window.StyleFix) {
1202
+ this.window.StyleFix.link(clone);
1184
1203
  }
1185
1204
  }
1186
- for (const sheet of Array.from(this.document.styleSheets)) {
1187
- this.reloadStylesheetImages(sheet, path);
1188
- }
1189
- this.console.log(`Image reload: ${path}`);
1190
- }
1191
- reloadStylesheetImages(styleSheet, path) {
1192
- let rules;
1193
- try {
1194
- rules = (styleSheet || {}).cssRules;
1195
- } catch (e) {
1196
- return;
1205
+ reloadPage() {
1206
+ this.window.location.reload();
1197
1207
  }
1198
- if (!rules)
1199
- return;
1200
- const bgStyleNames = ["backgroundImage", "borderImage", "webkitBorderImage", "MozBorderImage"];
1201
- for (const rule of Array.from(rules)) {
1202
- switch (rule.type) {
1203
- case CSSRule.IMPORT_RULE:
1204
- this.reloadStylesheetImages(rule.styleSheet, path);
1205
- break;
1206
- case CSSRule.STYLE_RULE:
1207
- this.reloadStyleImages(rule.style, bgStyleNames, path);
1208
- break;
1209
- case CSSRule.MEDIA_RULE:
1210
- this.reloadStylesheetImages(rule, path);
1211
- break;
1208
+ reloadImages(path) {
1209
+ for (const img of Array.from(this.document.images)) {
1210
+ if (this.pathsMatch(path, pathFromUrl(img.src))) {
1211
+ img.src = generateCacheBustUrl(img.src);
1212
+ }
1212
1213
  }
1213
- }
1214
- }
1215
- reloadStyleImages(style, styleNames, path) {
1216
- for (const styleName of styleNames) {
1217
- const value = style[styleName];
1218
- if (typeof value === "string") {
1219
- const newValue = value.replace(/\burl\s*\(([^)]*)\)/g, (match, src) => {
1220
- const cleanSrc = src.replace(/^['"]|['"]$/g, "");
1221
- if (this.pathsMatch(path, pathFromUrl(cleanSrc))) {
1222
- return `url(${generateCacheBustUrl(cleanSrc)})`;
1223
- }
1224
- return match;
1225
- });
1226
- if (newValue !== value) {
1227
- style[styleName] = newValue;
1214
+ const bgSelectors = ["background", "border"];
1215
+ const bgStyleNames = ["backgroundImage", "borderImage", "webkitBorderImage", "MozBorderImage"];
1216
+ for (const selector of bgSelectors) {
1217
+ for (const el of Array.from(this.document.querySelectorAll(`[style*=${selector}]`))) {
1218
+ this.reloadStyleImages(el.style, bgStyleNames, path);
1228
1219
  }
1229
1220
  }
1221
+ for (const sheet of Array.from(this.document.styleSheets)) {
1222
+ this.reloadStylesheetImages(sheet, path);
1223
+ }
1224
+ this.console.log(`Image reload: ${path}`);
1230
1225
  }
1231
- }
1232
- pathsMatch(path1, path2) {
1233
- const segs1 = path1.replace(/^\//, "").split("/").reverse();
1234
- const segs2 = path2.replace(/^\//, "").split("/").reverse();
1235
- const len = Math.min(segs1.length, segs2.length);
1236
- for (let i = 0;i < len; i++) {
1237
- if (segs1[i] !== segs2[i])
1238
- return false;
1239
- }
1240
- return len > 0;
1241
- }
1242
- linkHref(link) {
1243
- return link.href || link.getAttribute && link.getAttribute("data-href");
1244
- }
1245
- collectImportedStylesheets(link, styleSheet, result) {
1246
- let rules;
1247
- try {
1248
- rules = (styleSheet || {}).cssRules;
1249
- } catch (e) {
1250
- return;
1251
- }
1252
- if (rules && rules.length) {
1253
- for (let index = 0;index < rules.length; index++) {
1254
- const rule = rules[index];
1226
+ reloadStylesheetImages(styleSheet, path) {
1227
+ let rules;
1228
+ try {
1229
+ rules = (styleSheet || {}).cssRules;
1230
+ } catch (e) {
1231
+ return;
1232
+ }
1233
+ if (!rules)
1234
+ return;
1235
+ const bgStyleNames = ["backgroundImage", "borderImage", "webkitBorderImage", "MozBorderImage"];
1236
+ for (const rule of Array.from(rules)) {
1255
1237
  switch (rule.type) {
1256
- case CSSRule.CHARSET_RULE:
1257
- continue;
1258
1238
  case CSSRule.IMPORT_RULE:
1259
- result.push({ link, rule, index, href: rule.href });
1260
- this.collectImportedStylesheets(link, rule.styleSheet, result);
1239
+ this.reloadStylesheetImages(rule.styleSheet, path);
1240
+ break;
1241
+ case CSSRule.STYLE_RULE:
1242
+ this.reloadStyleImages(rule.style, bgStyleNames, path);
1261
1243
  break;
1262
- default:
1244
+ case CSSRule.MEDIA_RULE:
1245
+ this.reloadStylesheetImages(rule, path);
1263
1246
  break;
1264
1247
  }
1265
1248
  }
1266
1249
  }
1267
- }
1268
- async reattachImportedRule({ rule, index, link }) {
1269
- const parent = rule.parentStyleSheet;
1270
- const href = generateCacheBustUrl(rule.href);
1271
- let media = "";
1272
- try {
1273
- media = rule.media.length ? [].join.call(rule.media, ", ") : "";
1274
- } catch (e) {
1275
- if (e.name !== "SecurityError") {
1276
- this.console.error(`Unexpected error accessing @import media: ${e.name}: ${e.message}`);
1250
+ reloadStyleImages(style, styleNames, path) {
1251
+ for (const styleName of styleNames) {
1252
+ const value = style[styleName];
1253
+ if (typeof value === "string") {
1254
+ const newValue = value.replace(/\burl\s*\(([^)]*)\)/g, (match, src) => {
1255
+ const cleanSrc = src.replace(/^['"]|['"]$/g, "");
1256
+ if (this.pathsMatch(path, pathFromUrl(cleanSrc))) {
1257
+ return `url(${generateCacheBustUrl(cleanSrc)})`;
1258
+ }
1259
+ return match;
1260
+ });
1261
+ if (newValue !== value) {
1262
+ style[styleName] = newValue;
1263
+ }
1264
+ }
1277
1265
  }
1278
1266
  }
1279
- const newRule = `@import url("${href}") ${media};`;
1280
- rule.__LiveReload_newHref = href;
1281
- if (this.importCacheWaitPeriod > 0) {
1282
- const tempLink = this.document.createElement("link");
1283
- tempLink.rel = "stylesheet";
1284
- tempLink.href = href;
1285
- tempLink.__LiveReload_pendingRemoval = true;
1286
- if (link.parentNode) {
1287
- link.parentNode.insertBefore(tempLink, link);
1288
- }
1289
- await new Promise((resolve) => this.Timer.start(this.importCacheWaitPeriod, resolve));
1290
- if (tempLink.parentNode) {
1291
- tempLink.parentNode.removeChild(tempLink);
1267
+ pathsMatch(path1, path2) {
1268
+ const segs1 = path1.replace(/^\//, "").split("/").reverse();
1269
+ const segs2 = path2.replace(/^\//, "").split("/").reverse();
1270
+ const len = Math.min(segs1.length, segs2.length);
1271
+ for (let i = 0;i < len; i++) {
1272
+ if (segs1[i] !== segs2[i])
1273
+ return false;
1292
1274
  }
1293
- if (rule.__LiveReload_newHref !== href) {
1275
+ return len > 0;
1276
+ }
1277
+ linkHref(link) {
1278
+ return link.href || link.getAttribute && link.getAttribute("data-href");
1279
+ }
1280
+ collectImportedStylesheets(link, styleSheet, result) {
1281
+ let rules;
1282
+ try {
1283
+ rules = (styleSheet || {}).cssRules;
1284
+ } catch (e) {
1294
1285
  return;
1295
1286
  }
1287
+ if (rules && rules.length) {
1288
+ for (let index = 0;index < rules.length; index++) {
1289
+ const rule = rules[index];
1290
+ switch (rule.type) {
1291
+ case CSSRule.CHARSET_RULE:
1292
+ continue;
1293
+ case CSSRule.IMPORT_RULE:
1294
+ result.push({ link, rule, index, href: rule.href });
1295
+ this.collectImportedStylesheets(link, rule.styleSheet, result);
1296
+ break;
1297
+ default:
1298
+ break;
1299
+ }
1300
+ }
1301
+ }
1296
1302
  }
1297
- parent.insertRule(newRule, index);
1298
- parent.deleteRule(index + 1);
1299
- if (this.importCacheWaitPeriod > 0) {
1300
- const updatedRule = parent.cssRules[index];
1301
- updatedRule.__LiveReload_newHref = href;
1302
- await new Promise((resolve) => this.Timer.start(this.importCacheWaitPeriod, resolve));
1303
- if (updatedRule.__LiveReload_newHref !== href) {
1304
- return;
1303
+ async reattachImportedRule({ rule, index, link }) {
1304
+ const parent = rule.parentStyleSheet;
1305
+ const href = generateCacheBustUrl(rule.href);
1306
+ let media = "";
1307
+ try {
1308
+ media = rule.media.length ? [].join.call(rule.media, ", ") : "";
1309
+ } catch (e) {
1310
+ if (e.name !== "SecurityError") {
1311
+ this.console.error(`Unexpected error accessing @import media: ${e.name}: ${e.message}`);
1312
+ }
1313
+ }
1314
+ const newRule = `@import url("${href}") ${media};`;
1315
+ rule.__LiveReload_newHref = href;
1316
+ if (this.importCacheWaitPeriod > 0) {
1317
+ const tempLink = this.document.createElement("link");
1318
+ tempLink.rel = "stylesheet";
1319
+ tempLink.href = href;
1320
+ tempLink.__LiveReload_pendingRemoval = true;
1321
+ if (link.parentNode) {
1322
+ link.parentNode.insertBefore(tempLink, link);
1323
+ }
1324
+ await new Promise((resolve) => this.Timer.start(this.importCacheWaitPeriod, resolve));
1325
+ if (tempLink.parentNode) {
1326
+ tempLink.parentNode.removeChild(tempLink);
1327
+ }
1328
+ if (rule.__LiveReload_newHref !== href) {
1329
+ return;
1330
+ }
1305
1331
  }
1306
1332
  parent.insertRule(newRule, index);
1307
1333
  parent.deleteRule(index + 1);
1334
+ if (this.importCacheWaitPeriod > 0) {
1335
+ const updatedRule = parent.cssRules[index];
1336
+ updatedRule.__LiveReload_newHref = href;
1337
+ await new Promise((resolve) => this.Timer.start(this.importCacheWaitPeriod, resolve));
1338
+ if (updatedRule.__LiveReload_newHref !== href) {
1339
+ return;
1340
+ }
1341
+ parent.insertRule(newRule, index);
1342
+ parent.deleteRule(index + 1);
1343
+ }
1308
1344
  }
1309
1345
  }
1310
- }
1311
1346
 
1312
- // src/live-morph.js
1313
- class LiveMorph {
1314
- constructor(window2) {
1315
- this.window = window2;
1316
- this.listeners = {};
1317
- if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) {
1318
- console.error("[LiveMorph] Disabled because the browser does not support WebSockets");
1319
- return;
1320
- }
1321
- this.options = Options.extract(this.window.document);
1322
- if (!this.options) {
1323
- console.error("[LiveMorph] Disabled - no configuration found");
1324
- console.error('[LiveMorph] Set window.LiveMorphOptions = { host: "localhost", port: 35729 }');
1325
- return;
1326
- }
1327
- console.log("[LiveMorph] Options loaded:", JSON.stringify({
1328
- host: this.options.host,
1329
- port: this.options.port,
1330
- morphHTML: this.options.morphHTML,
1331
- verbose: this.options.verbose
1332
- }));
1333
- this.console = this._setupConsole();
1334
- this.morpher = new Morpher(this.window, this.console, Timer, this.options.importCacheWaitPeriod);
1335
- this.connector = new Connector(this.options, this.WebSocket, Timer, {
1336
- connecting: () => {},
1337
- socketConnected: () => {},
1338
- connected: (protocol) => {
1339
- if (typeof this.listeners.connect === "function") {
1340
- this.listeners.connect();
1341
- }
1342
- const { host } = this.options;
1343
- const port = this.options.port ? `:${this.options.port}` : "";
1344
- this.log(`Connected to ${host}${port} (protocol v${protocol})`);
1345
- return this.sendInfo();
1346
- },
1347
- error: (e) => {
1348
- if (e instanceof ProtocolError) {
1349
- return console.log(`[LiveMorph] ${e.message}`);
1350
- } else {
1351
- return console.log(`[LiveMorph] Internal error: ${e.message}`);
1352
- }
1353
- },
1354
- disconnected: (reason, nextDelay) => {
1355
- if (typeof this.listeners.disconnect === "function") {
1356
- this.listeners.disconnect();
1357
- }
1358
- const { host } = this.options;
1359
- const port = this.options.port ? `:${this.options.port}` : "";
1360
- const delaySec = (nextDelay / 1000).toFixed(0);
1361
- switch (reason) {
1362
- case "cannot-connect":
1363
- return this.log(`Cannot connect to ${host}${port}, will retry in ${delaySec} sec`);
1364
- case "broken":
1365
- return this.log(`Disconnected from ${host}${port}, reconnecting in ${delaySec} sec`);
1366
- case "handshake-timeout":
1367
- return this.log(`Cannot connect to ${host}${port} (handshake timeout), will retry in ${delaySec} sec`);
1368
- case "handshake-failed":
1369
- return this.log(`Cannot connect to ${host}${port} (handshake failed), will retry in ${delaySec} sec`);
1370
- case "manual":
1371
- case "error":
1372
- default:
1373
- return this.log(`Disconnected from ${host}${port} (${reason}), reconnecting in ${delaySec} sec`);
1374
- }
1375
- },
1376
- message: (message) => {
1377
- switch (message.command) {
1378
- case "reload":
1379
- return this.performReload(message);
1380
- case "alert":
1381
- return this.performAlert(message);
1347
+ // src/live-morph.js
1348
+ class LiveMorph {
1349
+ constructor(window2) {
1350
+ this.window = window2;
1351
+ this.listeners = {};
1352
+ if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) {
1353
+ console.error("[LiveMorph] Disabled because the browser does not support WebSockets");
1354
+ return;
1355
+ }
1356
+ this.options = Options.extract(this.window.document);
1357
+ if (!this.options) {
1358
+ console.error("[LiveMorph] Disabled - no configuration found");
1359
+ console.error('[LiveMorph] Set window.LiveMorphOptions = { host: "localhost", port: 35729 }');
1360
+ return;
1361
+ }
1362
+ console.log("[LiveMorph] Options loaded:", JSON.stringify({
1363
+ host: this.options.host,
1364
+ port: this.options.port,
1365
+ morphHTML: this.options.morphHTML,
1366
+ verbose: this.options.verbose
1367
+ }));
1368
+ this.console = this._setupConsole();
1369
+ this.morpher = new Morpher(this.window, this.console, Timer, this.options.importCacheWaitPeriod);
1370
+ this.connector = new Connector(this.options, this.WebSocket, Timer, {
1371
+ connecting: () => {},
1372
+ socketConnected: () => {},
1373
+ connected: (protocol) => {
1374
+ if (typeof this.listeners.connect === "function") {
1375
+ this.listeners.connect();
1376
+ }
1377
+ const { host } = this.options;
1378
+ const port = this.options.port ? `:${this.options.port}` : "";
1379
+ this.log(`Connected to ${host}${port} (protocol v${protocol})`);
1380
+ return this.sendInfo();
1381
+ },
1382
+ error: (e) => {
1383
+ if (e instanceof ProtocolError) {
1384
+ return console.log(`[LiveMorph] ${e.message}`);
1385
+ } else {
1386
+ return console.log(`[LiveMorph] Internal error: ${e.message}`);
1387
+ }
1388
+ },
1389
+ disconnected: (reason, nextDelay) => {
1390
+ if (typeof this.listeners.disconnect === "function") {
1391
+ this.listeners.disconnect();
1392
+ }
1393
+ const { host } = this.options;
1394
+ const port = this.options.port ? `:${this.options.port}` : "";
1395
+ const delaySec = (nextDelay / 1000).toFixed(0);
1396
+ switch (reason) {
1397
+ case "cannot-connect":
1398
+ return this.log(`Cannot connect to ${host}${port}, will retry in ${delaySec} sec`);
1399
+ case "broken":
1400
+ return this.log(`Disconnected from ${host}${port}, reconnecting in ${delaySec} sec`);
1401
+ case "handshake-timeout":
1402
+ return this.log(`Cannot connect to ${host}${port} (handshake timeout), will retry in ${delaySec} sec`);
1403
+ case "handshake-failed":
1404
+ return this.log(`Cannot connect to ${host}${port} (handshake failed), will retry in ${delaySec} sec`);
1405
+ case "manual":
1406
+ case "error":
1407
+ default:
1408
+ return this.log(`Disconnected from ${host}${port} (${reason}), reconnecting in ${delaySec} sec`);
1409
+ }
1410
+ },
1411
+ message: (message) => {
1412
+ switch (message.command) {
1413
+ case "reload":
1414
+ return this.performReload(message);
1415
+ case "alert":
1416
+ return this.performAlert(message);
1417
+ }
1382
1418
  }
1419
+ });
1420
+ this.initialized = true;
1421
+ }
1422
+ _setupConsole() {
1423
+ const hasConsole = this.window.console && this.window.console.log && this.window.console.error;
1424
+ if (!hasConsole) {
1425
+ return { log() {}, error() {} };
1383
1426
  }
1384
- });
1385
- this.initialized = true;
1386
- }
1387
- _setupConsole() {
1388
- const hasConsole = this.window.console && this.window.console.log && this.window.console.error;
1389
- if (!hasConsole) {
1390
- return { log() {}, error() {} };
1427
+ if (this.options.verbose) {
1428
+ return this.window.console;
1429
+ }
1430
+ return {
1431
+ log() {},
1432
+ error: this.window.console.error.bind(this.window.console)
1433
+ };
1391
1434
  }
1392
- if (this.options.verbose) {
1393
- return this.window.console;
1435
+ on(eventName, handler) {
1436
+ this.listeners[eventName] = handler;
1394
1437
  }
1395
- return {
1396
- log() {},
1397
- error: this.window.console.error.bind(this.window.console)
1398
- };
1399
- }
1400
- on(eventName, handler) {
1401
- this.listeners[eventName] = handler;
1402
- }
1403
- log(message) {
1404
- return this.console.log(`[LiveMorph] ${message}`);
1405
- }
1406
- performReload(message) {
1407
- this.log(`Received reload request for: ${message.path}`);
1408
- const options = {
1409
- liveCSS: message.liveCSS != null ? message.liveCSS : true,
1410
- liveImg: message.liveImg != null ? message.liveImg : true,
1411
- reloadMissingCSS: message.reloadMissingCSS != null ? message.reloadMissingCSS : true,
1412
- morphHTML: this.options.morphHTML
1413
- };
1414
- this.log(`Reload options: ${JSON.stringify(options)}`);
1415
- return this.morpher.reload(message.path, options);
1416
- }
1417
- performAlert(message) {
1418
- return alert(message.message);
1419
- }
1420
- sendInfo() {
1421
- if (!this.initialized) {
1422
- return;
1438
+ log(message) {
1439
+ return this.console.log(`[LiveMorph] ${message}`);
1423
1440
  }
1424
- if (!(this.connector.protocol >= 7)) {
1425
- return;
1441
+ performReload(message) {
1442
+ this.log(`Received reload request for: ${message.path}`);
1443
+ const options = {
1444
+ liveCSS: message.liveCSS != null ? message.liveCSS : true,
1445
+ liveImg: message.liveImg != null ? message.liveImg : true,
1446
+ reloadMissingCSS: message.reloadMissingCSS != null ? message.reloadMissingCSS : true,
1447
+ morphHTML: this.options.morphHTML
1448
+ };
1449
+ this.log(`Reload options: ${JSON.stringify(options)}`);
1450
+ return this.morpher.reload(message.path, options);
1426
1451
  }
1427
- this.connector.sendCommand({
1428
- command: "info",
1429
- plugins: {},
1430
- url: this.window.location.href
1431
- });
1432
- }
1433
- shutDown() {
1434
- if (!this.initialized) {
1435
- return;
1452
+ performAlert(message) {
1453
+ return alert(message.message);
1454
+ }
1455
+ sendInfo() {
1456
+ if (!this.initialized) {
1457
+ return;
1458
+ }
1459
+ if (!(this.connector.protocol >= 7)) {
1460
+ return;
1461
+ }
1462
+ this.connector.sendCommand({
1463
+ command: "info",
1464
+ plugins: {},
1465
+ url: this.window.location.href
1466
+ });
1436
1467
  }
1437
- this.connector.disconnect();
1438
- this.log("Disconnected");
1439
- if (typeof this.listeners.shutdown === "function") {
1440
- this.listeners.shutdown();
1468
+ shutDown() {
1469
+ if (!this.initialized) {
1470
+ return;
1471
+ }
1472
+ this.connector.disconnect();
1473
+ this.log("Disconnected");
1474
+ if (typeof this.listeners.shutdown === "function") {
1475
+ this.listeners.shutdown();
1476
+ }
1441
1477
  }
1442
1478
  }
1443
- }
1444
1479
 
1445
- // src/index.js
1446
- var liveMorph = new LiveMorph(window);
1447
- window.LiveMorph = liveMorph;
1448
- if (typeof document !== "undefined") {
1449
- document.addEventListener("LiveMorphShutDown", () => {
1450
- liveMorph.shutDown();
1451
- });
1452
- liveMorph.on("connect", () => {
1453
- const event = new CustomEvent("LiveMorphConnect");
1454
- document.dispatchEvent(event);
1455
- });
1456
- liveMorph.on("disconnect", () => {
1457
- const event = new CustomEvent("LiveMorphDisconnect");
1458
- document.dispatchEvent(event);
1459
- });
1460
- }
1461
- var src_default = liveMorph;
1462
- export {
1463
- src_default as default
1464
- };
1480
+ // src/index.js
1481
+ var liveMorph = new LiveMorph(window);
1482
+ window.LiveMorph = liveMorph;
1483
+ if (typeof document !== "undefined") {
1484
+ document.addEventListener("LiveMorphShutDown", () => {
1485
+ liveMorph.shutDown();
1486
+ });
1487
+ liveMorph.on("connect", () => {
1488
+ const event = new CustomEvent("LiveMorphConnect");
1489
+ document.dispatchEvent(event);
1490
+ });
1491
+ liveMorph.on("disconnect", () => {
1492
+ const event = new CustomEvent("LiveMorphDisconnect");
1493
+ document.dispatchEvent(event);
1494
+ });
1495
+ }
1496
+ var src_default = liveMorph;
1497
+ })();