hotwire-spark 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -10
- data/app/assets/javascripts/hotwire_spark.js +101 -69
- data/app/assets/javascripts/hotwire_spark.min.js +1 -1
- data/app/assets/javascripts/hotwire_spark.min.js.map +1 -1
- data/app/controllers/hotwire/spark/source_files_controller.rb +14 -0
- data/app/javascript/hotwire/spark/channels/monitoring_channel.js +10 -9
- data/app/javascript/hotwire/spark/index.js +10 -2
- data/app/javascript/hotwire/spark/reloaders/{html_reloader.js → morph_html_reloader.js} +8 -8
- data/app/javascript/hotwire/spark/reloaders/replace_html_reloader.js +31 -0
- data/app/javascript/hotwire/spark/reloaders/stimulus_reloader.js +49 -59
- data/config/routes.rb +1 -0
- data/lib/hotwire/spark/default_options.rb +36 -0
- data/lib/hotwire/spark/engine.rb +2 -5
- data/lib/hotwire/spark/file_watcher.rb +5 -1
- data/lib/hotwire/spark/installer.rb +17 -6
- data/lib/hotwire/spark/middleware.rb +12 -7
- data/lib/hotwire/spark/version.rb +1 -1
- data/lib/hotwire-spark.rb +6 -3
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86280ef674a47bc00119c73166435a363c34e6c22804444df25d8f909fe5755c
|
4
|
+
data.tar.gz: 016b6bc8f6bf0f22bf3cec4baa754a167fdc526230f0a57df83c1f0916ee4f56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be78aab02729c9f00b86f366ce7ff2813dab842a2e1b78699d19dfaaf11b61a98e31269cb625939c4a8204fa3c6c3b0f51548098ff4df3cd78d1eee91dfd26b6
|
7
|
+
data.tar.gz: ef71e4ecbb78de1dcfe7b2224e40b7d41455a2da4feb3cbf0967664acd7d8eb8a9e5b3cae7a9fbba48c1647eda661249d7c9866de595e12cc186a4471323eb8c
|
data/README.md
CHANGED
@@ -24,12 +24,25 @@ That's it!
|
|
24
24
|
|
25
25
|
The system will listen for three kinds of changes and will take action depending on each:
|
26
26
|
|
27
|
-
*
|
27
|
+
* HTML contents
|
28
|
+
* CSS
|
29
|
+
* Stimulus controllers
|
30
|
+
|
31
|
+
Depending on your setup, the default behavior will be different.
|
32
|
+
|
33
|
+
### Importmaps
|
34
|
+
|
35
|
+
Importmaps allows for the smoother updates because it supports hot-reloading for Stimulus controllers:
|
36
|
+
|
37
|
+
* **HTML change:** it fetches the new document body and updates the current body with morphing, then it reloads the Stimulus controllers in the page. It uses [`idiomorph`](https://github.com/bigskysoftware/idiomorph) under the hood.
|
28
38
|
* **CSS change:** it fetches and reloads the stylesheet that changed.
|
29
39
|
* **Stimulus controller change:** it fetches the Stimulus controller that changed and reloads all the controllers in the page.
|
30
40
|
|
31
|
-
|
32
|
-
|
41
|
+
### JavaScript Bundling
|
42
|
+
|
43
|
+
* **HTML change:** it reloads the page with a Turbo visit.
|
44
|
+
* **CSS change:** it fetches and reloads the stylesheet that changed.
|
45
|
+
* **Stimulus controller change:** it reloads the page with a Turbo visit.
|
33
46
|
|
34
47
|
## Configuration
|
35
48
|
|
@@ -39,13 +52,17 @@ You can set configuration options on your `development.rb`. For example:
|
|
39
52
|
config.hotwire.spark.html_paths += %w[ lib ]
|
40
53
|
```
|
41
54
|
|
42
|
-
| Name
|
43
|
-
|
44
|
-
| `html_paths`
|
45
|
-
| `css_paths`
|
46
|
-
| `stimulus_paths`
|
47
|
-
| `enabled`
|
48
|
-
| `logging`
|
55
|
+
| Name | Description |
|
56
|
+
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
57
|
+
| `html_paths` | Paths where file changes trigger a content refresh. By default: `app/controllers`, `app/helpers`, `app/models`, `app/views`. |
|
58
|
+
| `css_paths` | Paths where file changes trigger a CSS refresh. By default: `app/assets/stylesheets` or `app/assets/builds` if exists. |
|
59
|
+
| `stimulus_paths` | Paths where file changes trigger a Stimulus controller refresh. By default: `app/javascript/controllers`. |
|
60
|
+
| `enabled` | Enable or disable live reloading. By default, it's only enabled in `development`. |
|
61
|
+
| `logging` | Show logs in the browser console when reloading happens. It's false by default. |
|
62
|
+
| `html_reload_method` | How to perform reloads when HTML content changes: `:morph` or `:replace`. By default, it is `:morph` and it will morph the `<body>` of the page and reload all the stimulus controllers. Set to `:replace` to reload the page with a regular Turbo navigation that will replace the `<body>`. |
|
63
|
+
| `html_extensions` | The extension to monitor for HTML content changes. By default: `rb`, `erb`. | |
|
64
|
+
| `css_extensions` | The extension to monitor for CSS changes. By default: `css`. | |
|
65
|
+
| `stimulus_extensions` | The extension to monitor for CSS changes. By default: `js`. | |
|
49
66
|
|
50
67
|
## License
|
51
68
|
|
@@ -1387,7 +1387,7 @@ var HotwireSpark = (function () {
|
|
1387
1387
|
})();
|
1388
1388
|
|
1389
1389
|
function log() {
|
1390
|
-
if (HotwireSpark.config.loggingEnabled) {
|
1390
|
+
if (HotwireSpark$1.config.loggingEnabled) {
|
1391
1391
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
1392
1392
|
args[_key] = arguments[_key];
|
1393
1393
|
}
|
@@ -1396,71 +1396,64 @@ var HotwireSpark = (function () {
|
|
1396
1396
|
}
|
1397
1397
|
|
1398
1398
|
class StimulusReloader {
|
1399
|
-
static async reload(
|
1400
|
-
|
1401
|
-
return new StimulusReloader(document, filePattern).reload();
|
1399
|
+
static async reload(path) {
|
1400
|
+
return new StimulusReloader(path).reload();
|
1402
1401
|
}
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1402
|
+
static async reloadAll() {
|
1403
|
+
Stimulus.controllers.forEach(controller => {
|
1404
|
+
Stimulus.unload(controller.identifier);
|
1405
|
+
Stimulus.register(controller.identifier, controller.constructor);
|
1406
|
+
});
|
1407
|
+
return Promise.resolve();
|
1408
|
+
}
|
1409
|
+
constructor(changedPath) {
|
1410
|
+
this.changedPath = changedPath;
|
1407
1411
|
this.application = window.Stimulus;
|
1408
1412
|
}
|
1409
1413
|
async reload() {
|
1410
1414
|
log("Reload Stimulus controllers...");
|
1411
1415
|
this.application.stop();
|
1412
|
-
|
1413
|
-
|
1416
|
+
try {
|
1417
|
+
await this.#reloadChangedController();
|
1418
|
+
} catch (error) {
|
1419
|
+
if (error instanceof SourceFileNotFound) {
|
1420
|
+
this.#deregisterChangedController();
|
1421
|
+
} else {
|
1422
|
+
console.error("Error reloading controller", error);
|
1423
|
+
}
|
1424
|
+
}
|
1414
1425
|
this.application.start();
|
1415
1426
|
}
|
1416
|
-
async #
|
1417
|
-
|
1418
|
-
|
1419
|
-
get #stimulusControllerPathsToReload() {
|
1420
|
-
this.controllerPathsToReload = this.controllerPathsToReload || this.#stimulusControllerPaths.filter(path => this.#shouldReloadController(path));
|
1421
|
-
return this.controllerPathsToReload;
|
1427
|
+
async #reloadChangedController() {
|
1428
|
+
const module = await this.#importControllerFromSource(this.changedPath);
|
1429
|
+
await this.#registerController(this.#changedControllerIdentifier, module);
|
1422
1430
|
}
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
return this.filePattern.test(path);
|
1428
|
-
}
|
1429
|
-
get #stimulusPathsByModule() {
|
1430
|
-
this.pathsByModule = this.pathsByModule || this.#parseImportmapJson();
|
1431
|
-
return this.pathsByModule;
|
1432
|
-
}
|
1433
|
-
#parseImportmapJson() {
|
1434
|
-
const importmapScript = this.document.querySelector("script[type=importmap]");
|
1435
|
-
return JSON.parse(importmapScript.text).imports;
|
1436
|
-
}
|
1437
|
-
async #reloadStimulusController(moduleName) {
|
1438
|
-
log(`\t${moduleName}`);
|
1439
|
-
const controllerName = this.#extractControllerName(moduleName);
|
1440
|
-
const path = cacheBustedUrl(this.#pathForModuleName(moduleName));
|
1441
|
-
const module = await import(path);
|
1442
|
-
this.#registerController(controllerName, module);
|
1443
|
-
}
|
1444
|
-
#unloadDeletedStimulusControllers() {
|
1445
|
-
this.#controllersToUnload.forEach(controller => this.#deregisterController(controller.identifier));
|
1446
|
-
}
|
1447
|
-
get #controllersToUnload() {
|
1448
|
-
if (this.#didChangeTriggerAReload) {
|
1449
|
-
return [];
|
1450
|
-
} else {
|
1451
|
-
return this.application.controllers.filter(controller => this.filePattern.test(`${controller.identifier}_controller`));
|
1431
|
+
async #importControllerFromSource(path) {
|
1432
|
+
const response = await fetch(`/spark/source_files/?path=${path}`);
|
1433
|
+
if (response.status === 404) {
|
1434
|
+
throw new SourceFileNotFound(`Source file not found: ${path}`);
|
1452
1435
|
}
|
1436
|
+
const sourceCode = await response.text();
|
1437
|
+
const blob = new Blob([sourceCode], {
|
1438
|
+
type: "application/javascript"
|
1439
|
+
});
|
1440
|
+
const moduleUrl = URL.createObjectURL(blob);
|
1441
|
+
const module = await import(moduleUrl);
|
1442
|
+
URL.revokeObjectURL(moduleUrl);
|
1443
|
+
return module;
|
1453
1444
|
}
|
1454
|
-
get #
|
1455
|
-
|
1456
|
-
|
1457
|
-
#pathForModuleName(moduleName) {
|
1458
|
-
return this.#stimulusPathsByModule[moduleName];
|
1445
|
+
get #changedControllerIdentifier() {
|
1446
|
+
this.changedControllerIdentifier = this.changedControllerIdentifier || this.#extractControllerName(this.changedPath);
|
1447
|
+
return this.changedControllerIdentifier;
|
1459
1448
|
}
|
1460
1449
|
#extractControllerName(path) {
|
1461
|
-
return path.replace(/^.*\//, "").replace("_controller", "").replace(/\//g, "--").replace(/_/g, "-");
|
1450
|
+
return path.replace(/^.*\//, "").replace("_controller", "").replace(/\//g, "--").replace(/_/g, "-").replace(/\.js$/, "");
|
1451
|
+
}
|
1452
|
+
#deregisterChangedController() {
|
1453
|
+
this.#deregisterController(this.#changedControllerIdentifier);
|
1462
1454
|
}
|
1463
1455
|
#registerController(name, module) {
|
1456
|
+
log("\tReloading controller", name);
|
1464
1457
|
this.application.unload(name);
|
1465
1458
|
this.application.register(name, module.default);
|
1466
1459
|
}
|
@@ -1469,17 +1462,18 @@ var HotwireSpark = (function () {
|
|
1469
1462
|
this.application.unload(name);
|
1470
1463
|
}
|
1471
1464
|
}
|
1465
|
+
class SourceFileNotFound extends Error {}
|
1472
1466
|
|
1473
|
-
class
|
1467
|
+
class MorphHtmlReloader {
|
1474
1468
|
static async reload() {
|
1475
|
-
return new
|
1469
|
+
return new MorphHtmlReloader().reload();
|
1476
1470
|
}
|
1477
1471
|
async reload() {
|
1478
|
-
|
1479
|
-
await this.#reloadStimulus(
|
1472
|
+
await this.#reloadHtml();
|
1473
|
+
await this.#reloadStimulus();
|
1480
1474
|
}
|
1481
1475
|
async #reloadHtml() {
|
1482
|
-
log("Reload html...");
|
1476
|
+
log("Reload html with morph...");
|
1483
1477
|
const reloadedDocument = await reloadHtmlDocument();
|
1484
1478
|
this.#updateBody(reloadedDocument.body);
|
1485
1479
|
return reloadedDocument;
|
@@ -1487,8 +1481,8 @@ var HotwireSpark = (function () {
|
|
1487
1481
|
#updateBody(newBody) {
|
1488
1482
|
Idiomorph.morph(document.body, newBody);
|
1489
1483
|
}
|
1490
|
-
async #reloadStimulus(
|
1491
|
-
|
1484
|
+
async #reloadStimulus() {
|
1485
|
+
await StimulusReloader.reloadAll();
|
1492
1486
|
}
|
1493
1487
|
}
|
1494
1488
|
|
@@ -1548,6 +1542,35 @@ var HotwireSpark = (function () {
|
|
1548
1542
|
}
|
1549
1543
|
}
|
1550
1544
|
|
1545
|
+
class ReplaceHtmlReloader {
|
1546
|
+
static async reload() {
|
1547
|
+
return new ReplaceHtmlReloader().reload();
|
1548
|
+
}
|
1549
|
+
async reload() {
|
1550
|
+
await this.#reloadHtml();
|
1551
|
+
}
|
1552
|
+
async #reloadHtml() {
|
1553
|
+
log("Reload html with Turbo...");
|
1554
|
+
this.#maintainScrollPosition();
|
1555
|
+
await this.#visitCurrentPage();
|
1556
|
+
}
|
1557
|
+
#maintainScrollPosition() {
|
1558
|
+
document.addEventListener("turbo:before-render", () => {
|
1559
|
+
Turbo.navigator.currentVisit.scrolled = true;
|
1560
|
+
}, {
|
1561
|
+
once: true
|
1562
|
+
});
|
1563
|
+
}
|
1564
|
+
#visitCurrentPage() {
|
1565
|
+
return new Promise(resolve => {
|
1566
|
+
document.addEventListener("turbo:load", () => resolve(document), {
|
1567
|
+
once: true
|
1568
|
+
});
|
1569
|
+
window.Turbo.visit(window.location);
|
1570
|
+
});
|
1571
|
+
}
|
1572
|
+
}
|
1573
|
+
|
1551
1574
|
consumer.subscriptions.create({
|
1552
1575
|
channel: "Hotwire::Spark::Channel"
|
1553
1576
|
}, {
|
@@ -1566,38 +1589,47 @@ var HotwireSpark = (function () {
|
|
1566
1589
|
action,
|
1567
1590
|
path
|
1568
1591
|
} = _ref;
|
1569
|
-
const fileName = assetNameFromPath(path);
|
1570
1592
|
switch (action) {
|
1571
1593
|
case "reload_html":
|
1572
1594
|
return this.reloadHtml();
|
1573
1595
|
case "reload_css":
|
1574
|
-
return this.reloadCss(
|
1596
|
+
return this.reloadCss(path);
|
1575
1597
|
case "reload_stimulus":
|
1576
|
-
return this.reloadStimulus(
|
1598
|
+
return this.reloadStimulus(path);
|
1577
1599
|
default:
|
1578
1600
|
throw new Error(`Unknown action: ${action}`);
|
1579
1601
|
}
|
1580
1602
|
},
|
1581
1603
|
reloadHtml() {
|
1582
|
-
|
1604
|
+
const htmlReloader = HotwireSpark.config.htmlReloadMethod == "morph" ? MorphHtmlReloader : ReplaceHtmlReloader;
|
1605
|
+
return htmlReloader.reload();
|
1583
1606
|
},
|
1584
|
-
reloadCss(
|
1607
|
+
reloadCss(path) {
|
1608
|
+
const fileName = assetNameFromPath(path);
|
1585
1609
|
return CssReloader.reload(new RegExp(fileName));
|
1586
1610
|
},
|
1587
|
-
reloadStimulus(
|
1588
|
-
return StimulusReloader.reload(
|
1611
|
+
reloadStimulus(path) {
|
1612
|
+
return StimulusReloader.reload(path);
|
1589
1613
|
}
|
1590
1614
|
});
|
1591
1615
|
|
1592
|
-
const HotwireSpark = {
|
1616
|
+
const HotwireSpark$1 = {
|
1593
1617
|
config: {
|
1594
|
-
loggingEnabled: false
|
1618
|
+
loggingEnabled: false,
|
1619
|
+
htmlReloadMethod: "morph"
|
1595
1620
|
}
|
1596
1621
|
};
|
1622
|
+
const configProperties = {
|
1623
|
+
loggingEnabled: "logging",
|
1624
|
+
htmlReloadMethod: "html-reload-method"
|
1625
|
+
};
|
1597
1626
|
document.addEventListener("DOMContentLoaded", function () {
|
1598
|
-
|
1627
|
+
Object.entries(configProperties).forEach(_ref => {
|
1628
|
+
let [key, property] = _ref;
|
1629
|
+
HotwireSpark$1.config[key] = getConfigurationProperty(property);
|
1630
|
+
});
|
1599
1631
|
});
|
1600
1632
|
|
1601
|
-
return HotwireSpark;
|
1633
|
+
return HotwireSpark$1;
|
1602
1634
|
|
1603
1635
|
})();
|
@@ -1,2 +1,2 @@
|
|
1
|
-
var HotwireSpark=function(){"use strict";var e={logger:"undefined"!=typeof console?console:void 0,WebSocket:"undefined"!=typeof WebSocket?WebSocket:void 0},t={log(...t){this.enabled&&(t.push(Date.now()),e.logger.log("[ActionCable]",...t))}};const n=()=>(new Date).getTime(),o=e=>(n()-e)/1e3;class i{constructor(e){this.visibilityDidChange=this.visibilityDidChange.bind(this),this.connection=e,this.reconnectAttempts=0}start(){this.isRunning()||(this.startedAt=n(),delete this.stoppedAt,this.startPolling(),addEventListener("visibilitychange",this.visibilityDidChange),t.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`))}stop(){this.isRunning()&&(this.stoppedAt=n(),this.stopPolling(),removeEventListener("visibilitychange",this.visibilityDidChange),t.log("ConnectionMonitor stopped"))}isRunning(){return this.startedAt&&!this.stoppedAt}recordMessage(){this.pingedAt=n()}recordConnect(){this.reconnectAttempts=0,delete this.disconnectedAt,t.log("ConnectionMonitor recorded connect")}recordDisconnect(){this.disconnectedAt=n(),t.log("ConnectionMonitor recorded disconnect")}startPolling(){this.stopPolling(),this.poll()}stopPolling(){clearTimeout(this.pollTimeout)}poll(){this.pollTimeout=setTimeout((()=>{this.reconnectIfStale(),this.poll()}),this.getPollInterval())}getPollInterval(){const{staleThreshold:e,reconnectionBackoffRate:t}=this.constructor;return 1e3*e*Math.pow(1+t,Math.min(this.reconnectAttempts,10))*(1+(0===this.reconnectAttempts?1:t)*Math.random())}reconnectIfStale(){this.connectionIsStale()&&(t.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${o(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`),this.reconnectAttempts++,this.disconnectedRecently()?t.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${o(this.disconnectedAt)} s`):(t.log("ConnectionMonitor reopening"),this.connection.reopen()))}get refreshedAt(){return this.pingedAt?this.pingedAt:this.startedAt}connectionIsStale(){return o(this.refreshedAt)>this.constructor.staleThreshold}disconnectedRecently(){return this.disconnectedAt&&o(this.disconnectedAt)<this.constructor.staleThreshold}visibilityDidChange(){"visible"===document.visibilityState&&setTimeout((()=>{!this.connectionIsStale()&&this.connection.isOpen()||(t.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`),this.connection.reopen())}),200)}}i.staleThreshold=6,i.reconnectionBackoffRate=.15;var r={message_types:{welcome:"welcome",disconnect:"disconnect",ping:"ping",confirmation:"confirm_subscription",rejection:"reject_subscription"},disconnect_reasons:{unauthorized:"unauthorized",invalid_request:"invalid_request",server_restart:"server_restart",remote:"remote"},default_mount_path:"/cable",protocols:["actioncable-v1-json","actioncable-unsupported"]};const{message_types:s,protocols:l}=r,c=l.slice(0,l.length-1),a=[].indexOf;class u{constructor(e){this.open=this.open.bind(this),this.consumer=e,this.subscriptions=this.consumer.subscriptions,this.monitor=new i(this),this.disconnected=!0}send(e){return!!this.isOpen()&&(this.webSocket.send(JSON.stringify(e)),!0)}open(){if(this.isActive())return t.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`),!1;{const n=[...l,...this.consumer.subprotocols||[]];return t.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${n}`),this.webSocket&&this.uninstallEventHandlers(),this.webSocket=new e.WebSocket(this.consumer.url,n),this.installEventHandlers(),this.monitor.start(),!0}}close({allowReconnect:e}={allowReconnect:!0}){if(e||this.monitor.stop(),this.isOpen())return this.webSocket.close()}reopen(){if(t.log(`Reopening WebSocket, current state is ${this.getState()}`),!this.isActive())return this.open();try{return this.close()}catch(e){t.log("Failed to reopen WebSocket",e)}finally{t.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`),setTimeout(this.open,this.constructor.reopenDelay)}}getProtocol(){if(this.webSocket)return this.webSocket.protocol}isOpen(){return this.isState("open")}isActive(){return this.isState("open","connecting")}triedToReconnect(){return this.monitor.reconnectAttempts>0}isProtocolSupported(){return a.call(c,this.getProtocol())>=0}isState(...e){return a.call(e,this.getState())>=0}getState(){if(this.webSocket)for(let t in e.WebSocket)if(e.WebSocket[t]===this.webSocket.readyState)return t.toLowerCase();return null}installEventHandlers(){for(let e in this.events){const t=this.events[e].bind(this);this.webSocket[`on${e}`]=t}}uninstallEventHandlers(){for(let e in this.events)this.webSocket[`on${e}`]=function(){}}}u.reopenDelay=500,u.prototype.events={message(e){if(!this.isProtocolSupported())return;const{identifier:n,message:o,reason:i,reconnect:r,type:l}=JSON.parse(e.data);switch(this.monitor.recordMessage(),l){case s.welcome:return this.triedToReconnect()&&(this.reconnectAttempted=!0),this.monitor.recordConnect(),this.subscriptions.reload();case s.disconnect:return t.log(`Disconnecting. Reason: ${i}`),this.close({allowReconnect:r});case s.ping:return null;case s.confirmation:return this.subscriptions.confirmSubscription(n),this.reconnectAttempted?(this.reconnectAttempted=!1,this.subscriptions.notify(n,"connected",{reconnected:!0})):this.subscriptions.notify(n,"connected",{reconnected:!1});case s.rejection:return this.subscriptions.reject(n);default:return this.subscriptions.notify(n,"received",o)}},open(){if(t.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`),this.disconnected=!1,!this.isProtocolSupported())return t.log("Protocol is unsupported. Stopping monitor and disconnecting."),this.close({allowReconnect:!1})},close(e){if(t.log("WebSocket onclose event"),!this.disconnected)return this.disconnected=!0,this.monitor.recordDisconnect(),this.subscriptions.notifyAll("disconnected",{willAttemptReconnect:this.monitor.isRunning()})},error(){t.log("WebSocket onerror event")}};class d{constructor(e,t={},n){this.consumer=e,this.identifier=JSON.stringify(t),function(e,t){if(null!=t)for(let n in t){const o=t[n];e[n]=o}}(this,n)}perform(e,t={}){return t.action=e,this.send(t)}send(e){return this.consumer.send({command:"message",identifier:this.identifier,data:JSON.stringify(e)})}unsubscribe(){return this.consumer.subscriptions.remove(this)}}class h{constructor(e){this.subscriptions=e,this.pendingSubscriptions=[]}guarantee(e){-1==this.pendingSubscriptions.indexOf(e)?(t.log(`SubscriptionGuarantor guaranteeing ${e.identifier}`),this.pendingSubscriptions.push(e)):t.log(`SubscriptionGuarantor already guaranteeing ${e.identifier}`),this.startGuaranteeing()}forget(e){t.log(`SubscriptionGuarantor forgetting ${e.identifier}`),this.pendingSubscriptions=this.pendingSubscriptions.filter((t=>t!==e))}startGuaranteeing(){this.stopGuaranteeing(),this.retrySubscribing()}stopGuaranteeing(){clearTimeout(this.retryTimeout)}retrySubscribing(){this.retryTimeout=setTimeout((()=>{this.subscriptions&&"function"==typeof this.subscriptions.subscribe&&this.pendingSubscriptions.map((e=>{t.log(`SubscriptionGuarantor resubscribing ${e.identifier}`),this.subscriptions.subscribe(e)}))}),500)}}class p{constructor(e){this.consumer=e,this.guarantor=new h(this),this.subscriptions=[]}create(e,t){const n="object"==typeof e?e:{channel:e},o=new d(this.consumer,n,t);return this.add(o)}add(e){return this.subscriptions.push(e),this.consumer.ensureActiveConnection(),this.notify(e,"initialized"),this.subscribe(e),e}remove(e){return this.forget(e),this.findAll(e.identifier).length||this.sendCommand(e,"unsubscribe"),e}reject(e){return this.findAll(e).map((e=>(this.forget(e),this.notify(e,"rejected"),e)))}forget(e){return this.guarantor.forget(e),this.subscriptions=this.subscriptions.filter((t=>t!==e)),e}findAll(e){return this.subscriptions.filter((t=>t.identifier===e))}reload(){return this.subscriptions.map((e=>this.subscribe(e)))}notifyAll(e,...t){return this.subscriptions.map((n=>this.notify(n,e,...t)))}notify(e,t,...n){let o;return o="string"==typeof e?this.findAll(e):[e],o.map((e=>"function"==typeof e[t]?e[t](...n):void 0))}subscribe(e){this.sendCommand(e,"subscribe")&&this.guarantor.guarantee(e)}confirmSubscription(e){t.log(`Subscription confirmed ${e}`),this.findAll(e).map((e=>this.guarantor.forget(e)))}sendCommand(e,t){const{identifier:n}=e;return this.consumer.send({command:t,identifier:n})}}class f{constructor(e){this._url=e,this.subscriptions=new p(this),this.connection=new u(this),this.subprotocols=[]}get url(){return function(e){"function"==typeof e&&(e=e());if(e&&!/^wss?:/i.test(e)){const t=document.createElement("a");return t.href=e,t.href=t.href,t.protocol=t.protocol.replace("http","ws"),t.href}return e}(this._url)}send(e){return this.connection.send(e)}connect(){return this.connection.open()}disconnect(){return this.connection.close({allowReconnect:!1})}ensureActiveConnection(){if(!this.connection.isActive())return this.connection.open()}addSubProtocol(e){this.subprotocols=[...this.subprotocols,e]}}var m=function(e=function(e){const t=document.head.querySelector(`meta[name='action-cable-${e}']`);if(t)return t.getAttribute("content")}("url")||r.default_mount_path){return new f(e)}("/hotwire-spark");function g(e){return e.replace(/-[a-z0-9]+\.(\w+)(\?.*)?$/,".$1")}function b(e,t){const n=new URL(e,window.location.origin);return Object.entries(t).forEach((e=>{let[t,o]=e;n.searchParams.set(t,o)})),n.toString()}function v(e){return b(e,{reload:Date.now()})}async function y(){let e=v(b(window.location.href,{hotwire_spark:"true"}));const t=await fetch(e,{headers:{Accept:"text/html"}});if(!t.ok)throw new Error(`${t.status} when fetching ${e}`);const n=await t.text();return(new DOMParser).parseFromString(n,"text/html")}var S=function(){let e=new Set,t={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:a,afterNodeAdded:a,beforeNodeMorphed:a,afterNodeMorphed:a,beforeNodeRemoved:a,afterNodeRemoved:a,beforeAttributeUpdated:a},head:{style:"merge",shouldPreserve:function(e){return"true"===e.getAttribute("im-preserve")},shouldReAppend:function(e){return"true"===e.getAttribute("im-re-append")},shouldRemove:a,afterHeadMorphed:a}};function n(e,t,o){if(o.head.block){let i=e.querySelector("head"),r=t.querySelector("head");if(i&&r){let s=c(r,i,o);return void Promise.all(s).then((function(){n(e,t,Object.assign(o,{head:{block:!1,ignore:!0}}))}))}}if("innerHTML"===o.morphStyle)return r(t,e,o),e.children;if("outerHTML"===o.morphStyle||null==o.morphStyle){let n=function(e,t,n){let o;o=e.firstChild;let i=o,r=0;for(;o;){let e=m(o,t,n);e>r&&(i=o,r=e),o=o.nextSibling}return i}(t,e,o),r=n?.previousSibling,s=n?.nextSibling,l=i(e,n,o);return n?function(e,t,n){let o=[],i=[];for(;null!=e;)o.push(e),e=e.previousSibling;for(;o.length>0;){let e=o.pop();i.push(e),t.parentElement.insertBefore(e,t)}i.push(t);for(;null!=n;)o.push(n),i.push(n),n=n.nextSibling;for(;o.length>0;)t.parentElement.insertBefore(o.pop(),t.nextSibling);return i}(r,l,s):[]}throw"Do not understand how to morph style "+o.morphStyle}function o(e,t){return t.ignoreActiveValue&&e===document.activeElement}function i(e,t,n){if(!n.ignoreActive||e!==document.activeElement)return null==t?!1===n.callbacks.beforeNodeRemoved(e)?e:(e.remove(),n.callbacks.afterNodeRemoved(e),null):d(e,t)?(!1===n.callbacks.beforeNodeMorphed(e,t)||(e instanceof HTMLHeadElement&&n.head.ignore||(e instanceof HTMLHeadElement&&"morph"!==n.head.style?c(t,e,n):(!function(e,t,n){let i=e.nodeType;if(1===i){const o=e.attributes,i=t.attributes;for(const e of o)s(e.name,t,"update",n)||t.getAttribute(e.name)!==e.value&&t.setAttribute(e.name,e.value);for(let o=i.length-1;0<=o;o--){const r=i[o];s(r.name,t,"remove",n)||(e.hasAttribute(r.name)||t.removeAttribute(r.name))}}8!==i&&3!==i||t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue);o(t,n)||function(e,t,n){if(e instanceof HTMLInputElement&&t instanceof HTMLInputElement&&"file"!==e.type){let o=e.value,i=t.value;l(e,t,"checked",n),l(e,t,"disabled",n),e.hasAttribute("value")?o!==i&&(s("value",t,"update",n)||(t.setAttribute("value",o),t.value=o)):s("value",t,"remove",n)||(t.value="",t.removeAttribute("value"))}else if(e instanceof HTMLOptionElement)l(e,t,"selected",n);else if(e instanceof HTMLTextAreaElement&&t instanceof HTMLTextAreaElement){let o=e.value,i=t.value;if(s("value",t,"update",n))return;o!==i&&(t.value=o),t.firstChild&&t.firstChild.nodeValue!==o&&(t.firstChild.nodeValue=o)}}(e,t,n)}(t,e,n),o(e,n)||r(t,e,n))),n.callbacks.afterNodeMorphed(e,t)),e):!1===n.callbacks.beforeNodeRemoved(e)||!1===n.callbacks.beforeNodeAdded(t)?e:(e.parentElement.replaceChild(t,e),n.callbacks.afterNodeAdded(t),n.callbacks.afterNodeRemoved(e),t)}function r(e,t,n){let o,r=e.firstChild,s=t.firstChild;for(;r;){if(o=r,r=o.nextSibling,null==s){if(!1===n.callbacks.beforeNodeAdded(o))return;t.appendChild(o),n.callbacks.afterNodeAdded(o),y(n,o);continue}if(u(o,s,n)){i(s,o,n),s=s.nextSibling,y(n,o);continue}let l=p(e,t,o,s,n);if(l){s=h(s,l,n),i(l,o,n),y(n,o);continue}let c=f(e,t,o,s,n);if(c)s=h(s,c,n),i(c,o,n),y(n,o);else{if(!1===n.callbacks.beforeNodeAdded(o))return;t.insertBefore(o,s),n.callbacks.afterNodeAdded(o),y(n,o)}}for(;null!==s;){let e=s;s=s.nextSibling,g(e,n)}}function s(e,t,n,o){return!("value"!==e||!o.ignoreActiveValue||t!==document.activeElement)||!1===o.callbacks.beforeAttributeUpdated(e,t,n)}function l(e,t,n,o){if(e[n]!==t[n]){let i=s(n,t,"update",o);i||(t[n]=e[n]),e[n]?i||t.setAttribute(n,e[n]):s(n,t,"remove",o)||t.removeAttribute(n)}}function c(e,t,n){let o=[],i=[],r=[],s=[],l=n.head.style,c=new Map;for(const t of e.children)c.set(t.outerHTML,t);for(const e of t.children){let t=c.has(e.outerHTML),o=n.head.shouldReAppend(e),a=n.head.shouldPreserve(e);t||a?o?i.push(e):(c.delete(e.outerHTML),r.push(e)):"append"===l?o&&(i.push(e),s.push(e)):!1!==n.head.shouldRemove(e)&&i.push(e)}s.push(...c.values());let a=[];for(const e of s){let i=document.createRange().createContextualFragment(e.outerHTML).firstChild;if(!1!==n.callbacks.beforeNodeAdded(i)){if(i.href||i.src){let e=null,t=new Promise((function(t){e=t}));i.addEventListener("load",(function(){e()})),a.push(t)}t.appendChild(i),n.callbacks.afterNodeAdded(i),o.push(i)}}for(const e of i)!1!==n.callbacks.beforeNodeRemoved(e)&&(t.removeChild(e),n.callbacks.afterNodeRemoved(e));return n.head.afterHeadMorphed(t,{added:o,kept:r,removed:i}),a}function a(){}function u(e,t,n){return null!=e&&null!=t&&(e.nodeType===t.nodeType&&e.tagName===t.tagName&&(""!==e.id&&e.id===t.id||S(n,e,t)>0))}function d(e,t){return null!=e&&null!=t&&(e.nodeType===t.nodeType&&e.tagName===t.tagName)}function h(e,t,n){for(;e!==t;){let t=e;e=e.nextSibling,g(t,n)}return y(n,t),t.nextSibling}function p(e,t,n,o,i){let r=S(i,n,t);if(r>0){let t=o,s=0;for(;null!=t;){if(u(n,t,i))return t;if(s+=S(i,t,e),s>r)return null;t=t.nextSibling}}return null}function f(e,t,n,o,i){let r=o,s=n.nextSibling,l=0;for(;null!=r;){if(S(i,r,e)>0)return null;if(d(n,r))return r;if(d(s,r)&&(l++,s=s.nextSibling,l>=2))return null;r=r.nextSibling}return r}function m(e,t,n){return d(e,t)?.5+S(n,e,t):0}function g(e,t){y(t,e),!1!==t.callbacks.beforeNodeRemoved(e)&&(e.remove(),t.callbacks.afterNodeRemoved(e))}function b(e,t){return!e.deadIds.has(t)}function v(t,n,o){return(t.idMap.get(o)||e).has(n)}function y(t,n){let o=t.idMap.get(n)||e;for(const e of o)t.deadIds.add(e)}function S(t,n,o){let i=t.idMap.get(n)||e,r=0;for(const e of i)b(t,e)&&v(t,e,o)&&++r;return r}function A(e,t){let n=e.parentElement,o=e.querySelectorAll("[id]");for(const e of o){let o=e;for(;o!==n&&null!=o;){let n=t.get(o);null==n&&(n=new Set,t.set(o,n)),n.add(e.id),o=o.parentElement}}}function w(e,t){let n=new Map;return A(e,n),A(t,n),n}return{morph:function(e,o,i={}){e instanceof Document&&(e=e.documentElement),"string"==typeof o&&(o=function(e){let t=new DOMParser,n=e.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,"");if(n.match(/<\/html>/)||n.match(/<\/head>/)||n.match(/<\/body>/)){let o=t.parseFromString(e,"text/html");if(n.match(/<\/html>/))return o.generatedByIdiomorph=!0,o;{let e=o.firstChild;return e?(e.generatedByIdiomorph=!0,e):null}}{let n=t.parseFromString("<body><template>"+e+"</template></body>","text/html").body.querySelector("template").content;return n.generatedByIdiomorph=!0,n}}(o));let r=function(e){if(null==e){return document.createElement("div")}if(e.generatedByIdiomorph)return e;if(e instanceof Node){const t=document.createElement("div");return t.append(e),t}{const t=document.createElement("div");for(const n of[...e])t.append(n);return t}}(o),s=function(e,n,o){return o=function(e){let n={};return Object.assign(n,t),Object.assign(n,e),n.callbacks={},Object.assign(n.callbacks,t.callbacks),Object.assign(n.callbacks,e.callbacks),n.head={},Object.assign(n.head,t.head),Object.assign(n.head,e.head),n}(o),{target:e,newContent:n,config:o,morphStyle:o.morphStyle,ignoreActive:o.ignoreActive,ignoreActiveValue:o.ignoreActiveValue,idMap:w(e,n),deadIds:new Set,callbacks:o.callbacks,head:o.head}}(e,r,i);return n(e,r,s)},defaults:t}}();function A(){if(R.config.loggingEnabled){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];console.log("[hotwire_spark]",...t)}}class w{static async reload(e){const t=await y();return new w(t,e).reload()}constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:/./;this.document=e,this.filePattern=t,this.application=window.Stimulus}async reload(){A("Reload Stimulus controllers..."),this.application.stop(),await this.#e(),this.#t(),this.application.start()}async#e(){await Promise.all(this.#n.map((async e=>this.#o(e))))}get#n(){return this.controllerPathsToReload=this.controllerPathsToReload||this.#i.filter((e=>this.#r(e))),this.controllerPathsToReload}get#i(){return Object.keys(this.#s).filter((e=>e.endsWith("_controller")))}#r(e){return this.filePattern.test(e)}get#s(){return this.pathsByModule=this.pathsByModule||this.#l(),this.pathsByModule}#l(){const e=this.document.querySelector("script[type=importmap]");return JSON.parse(e.text).imports}async#o(e){A(`\t${e}`);const t=this.#c(e),n=v(this.#a(e)),o=await import(n);this.#u(t,o)}#t(){this.#d.forEach((e=>this.#h(e.identifier)))}get#d(){return this.#p?[]:this.application.controllers.filter((e=>this.filePattern.test(`${e.identifier}_controller`)))}get#p(){return this.#n.length>0}#a(e){return this.#s[e]}#c(e){return e.replace(/^.*\//,"").replace("_controller","").replace(/\//g,"--").replace(/_/g,"-")}#u(e,t){this.application.unload(e),this.application.register(e,t.default)}#h(e){A(`\tRemoving controller ${e}`),this.application.unload(e)}}class k{static async reload(){return(new k).reload()}async reload(){const e=await this.#f();await this.#m(e)}async#f(){A("Reload html...");const e=await y();return this.#g(e.body),e}#g(e){S.morph(document.body,e)}async#m(e){return new w(e).reload()}}class C{static async reload(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return new C(...t).reload()}constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:/./;this.filePattern=e}async reload(){A("Reload css..."),await Promise.all(await this.#b())}async#b(){return(await this.#v()).map((e=>this.#y(e)))}async#v(){const e=await y();return Array.from(e.head.querySelectorAll("link[rel='stylesheet']"))}#y(e){return this.#S(e)?this.#A(e):Promise.resolve()}#S(e){return this.filePattern.test(e.getAttribute("href"))}async#A(e){return new Promise((t=>{const n=e.getAttribute("href"),o=this.#w(e)||this.#k(e);o.setAttribute("href",v(e.getAttribute("href"))),o.onload=()=>{A(`\t${n}`),t()}}))}#w(e){return this.#C.find((t=>g(e.href)===g(t.href)))}get#C(){return Array.from(document.querySelectorAll("link[rel='stylesheet']"))}#k(e){return document.head.append(e),e}}m.subscriptions.create({channel:"Hotwire::Spark::Channel"},{connected(){document.body.setAttribute("data-hotwire-spark-ready","")},async received(e){try{await this.dispatch(e)}catch(t){console.log(`Error on ${e.action}`,t)}},dispatch(e){let{action:t,path:n}=e;const o=function(e){return e.split("/").pop().split(".")[0]}(n);switch(t){case"reload_html":return this.reloadHtml();case"reload_css":return this.reloadCss(o);case"reload_stimulus":return this.reloadStimulus(o);default:throw new Error(`Unknown action: ${t}`)}},reloadHtml:()=>k.reload(),reloadCss:e=>C.reload(new RegExp(e)),reloadStimulus:e=>w.reload(new RegExp(e))});const R={config:{loggingEnabled:!1}};return document.addEventListener("DOMContentLoaded",(function(){var e;R.config.loggingEnabled=(e="logging",document.querySelector(`meta[name="hotwire-spark:${e}"]`)?.content)})),R}();
|
1
|
+
var HotwireSpark=function(){"use strict";var e={logger:"undefined"!=typeof console?console:void 0,WebSocket:"undefined"!=typeof WebSocket?WebSocket:void 0},t={log(...t){this.enabled&&(t.push(Date.now()),e.logger.log("[ActionCable]",...t))}};const n=()=>(new Date).getTime(),o=e=>(n()-e)/1e3;class i{constructor(e){this.visibilityDidChange=this.visibilityDidChange.bind(this),this.connection=e,this.reconnectAttempts=0}start(){this.isRunning()||(this.startedAt=n(),delete this.stoppedAt,this.startPolling(),addEventListener("visibilitychange",this.visibilityDidChange),t.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`))}stop(){this.isRunning()&&(this.stoppedAt=n(),this.stopPolling(),removeEventListener("visibilitychange",this.visibilityDidChange),t.log("ConnectionMonitor stopped"))}isRunning(){return this.startedAt&&!this.stoppedAt}recordMessage(){this.pingedAt=n()}recordConnect(){this.reconnectAttempts=0,delete this.disconnectedAt,t.log("ConnectionMonitor recorded connect")}recordDisconnect(){this.disconnectedAt=n(),t.log("ConnectionMonitor recorded disconnect")}startPolling(){this.stopPolling(),this.poll()}stopPolling(){clearTimeout(this.pollTimeout)}poll(){this.pollTimeout=setTimeout((()=>{this.reconnectIfStale(),this.poll()}),this.getPollInterval())}getPollInterval(){const{staleThreshold:e,reconnectionBackoffRate:t}=this.constructor;return 1e3*e*Math.pow(1+t,Math.min(this.reconnectAttempts,10))*(1+(0===this.reconnectAttempts?1:t)*Math.random())}reconnectIfStale(){this.connectionIsStale()&&(t.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${o(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`),this.reconnectAttempts++,this.disconnectedRecently()?t.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${o(this.disconnectedAt)} s`):(t.log("ConnectionMonitor reopening"),this.connection.reopen()))}get refreshedAt(){return this.pingedAt?this.pingedAt:this.startedAt}connectionIsStale(){return o(this.refreshedAt)>this.constructor.staleThreshold}disconnectedRecently(){return this.disconnectedAt&&o(this.disconnectedAt)<this.constructor.staleThreshold}visibilityDidChange(){"visible"===document.visibilityState&&setTimeout((()=>{!this.connectionIsStale()&&this.connection.isOpen()||(t.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`),this.connection.reopen())}),200)}}i.staleThreshold=6,i.reconnectionBackoffRate=.15;var r={message_types:{welcome:"welcome",disconnect:"disconnect",ping:"ping",confirmation:"confirm_subscription",rejection:"reject_subscription"},disconnect_reasons:{unauthorized:"unauthorized",invalid_request:"invalid_request",server_restart:"server_restart",remote:"remote"},default_mount_path:"/cable",protocols:["actioncable-v1-json","actioncable-unsupported"]};const{message_types:s,protocols:c}=r,l=c.slice(0,c.length-1),a=[].indexOf;class d{constructor(e){this.open=this.open.bind(this),this.consumer=e,this.subscriptions=this.consumer.subscriptions,this.monitor=new i(this),this.disconnected=!0}send(e){return!!this.isOpen()&&(this.webSocket.send(JSON.stringify(e)),!0)}open(){if(this.isActive())return t.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`),!1;{const n=[...c,...this.consumer.subprotocols||[]];return t.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${n}`),this.webSocket&&this.uninstallEventHandlers(),this.webSocket=new e.WebSocket(this.consumer.url,n),this.installEventHandlers(),this.monitor.start(),!0}}close({allowReconnect:e}={allowReconnect:!0}){if(e||this.monitor.stop(),this.isOpen())return this.webSocket.close()}reopen(){if(t.log(`Reopening WebSocket, current state is ${this.getState()}`),!this.isActive())return this.open();try{return this.close()}catch(e){t.log("Failed to reopen WebSocket",e)}finally{t.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`),setTimeout(this.open,this.constructor.reopenDelay)}}getProtocol(){if(this.webSocket)return this.webSocket.protocol}isOpen(){return this.isState("open")}isActive(){return this.isState("open","connecting")}triedToReconnect(){return this.monitor.reconnectAttempts>0}isProtocolSupported(){return a.call(l,this.getProtocol())>=0}isState(...e){return a.call(e,this.getState())>=0}getState(){if(this.webSocket)for(let t in e.WebSocket)if(e.WebSocket[t]===this.webSocket.readyState)return t.toLowerCase();return null}installEventHandlers(){for(let e in this.events){const t=this.events[e].bind(this);this.webSocket[`on${e}`]=t}}uninstallEventHandlers(){for(let e in this.events)this.webSocket[`on${e}`]=function(){}}}d.reopenDelay=500,d.prototype.events={message(e){if(!this.isProtocolSupported())return;const{identifier:n,message:o,reason:i,reconnect:r,type:c}=JSON.parse(e.data);switch(this.monitor.recordMessage(),c){case s.welcome:return this.triedToReconnect()&&(this.reconnectAttempted=!0),this.monitor.recordConnect(),this.subscriptions.reload();case s.disconnect:return t.log(`Disconnecting. Reason: ${i}`),this.close({allowReconnect:r});case s.ping:return null;case s.confirmation:return this.subscriptions.confirmSubscription(n),this.reconnectAttempted?(this.reconnectAttempted=!1,this.subscriptions.notify(n,"connected",{reconnected:!0})):this.subscriptions.notify(n,"connected",{reconnected:!1});case s.rejection:return this.subscriptions.reject(n);default:return this.subscriptions.notify(n,"received",o)}},open(){if(t.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`),this.disconnected=!1,!this.isProtocolSupported())return t.log("Protocol is unsupported. Stopping monitor and disconnecting."),this.close({allowReconnect:!1})},close(e){if(t.log("WebSocket onclose event"),!this.disconnected)return this.disconnected=!0,this.monitor.recordDisconnect(),this.subscriptions.notifyAll("disconnected",{willAttemptReconnect:this.monitor.isRunning()})},error(){t.log("WebSocket onerror event")}};class u{constructor(e,t={},n){this.consumer=e,this.identifier=JSON.stringify(t),function(e,t){if(null!=t)for(let n in t){const o=t[n];e[n]=o}}(this,n)}perform(e,t={}){return t.action=e,this.send(t)}send(e){return this.consumer.send({command:"message",identifier:this.identifier,data:JSON.stringify(e)})}unsubscribe(){return this.consumer.subscriptions.remove(this)}}class h{constructor(e){this.subscriptions=e,this.pendingSubscriptions=[]}guarantee(e){-1==this.pendingSubscriptions.indexOf(e)?(t.log(`SubscriptionGuarantor guaranteeing ${e.identifier}`),this.pendingSubscriptions.push(e)):t.log(`SubscriptionGuarantor already guaranteeing ${e.identifier}`),this.startGuaranteeing()}forget(e){t.log(`SubscriptionGuarantor forgetting ${e.identifier}`),this.pendingSubscriptions=this.pendingSubscriptions.filter((t=>t!==e))}startGuaranteeing(){this.stopGuaranteeing(),this.retrySubscribing()}stopGuaranteeing(){clearTimeout(this.retryTimeout)}retrySubscribing(){this.retryTimeout=setTimeout((()=>{this.subscriptions&&"function"==typeof this.subscriptions.subscribe&&this.pendingSubscriptions.map((e=>{t.log(`SubscriptionGuarantor resubscribing ${e.identifier}`),this.subscriptions.subscribe(e)}))}),500)}}class p{constructor(e){this.consumer=e,this.guarantor=new h(this),this.subscriptions=[]}create(e,t){const n="object"==typeof e?e:{channel:e},o=new u(this.consumer,n,t);return this.add(o)}add(e){return this.subscriptions.push(e),this.consumer.ensureActiveConnection(),this.notify(e,"initialized"),this.subscribe(e),e}remove(e){return this.forget(e),this.findAll(e.identifier).length||this.sendCommand(e,"unsubscribe"),e}reject(e){return this.findAll(e).map((e=>(this.forget(e),this.notify(e,"rejected"),e)))}forget(e){return this.guarantor.forget(e),this.subscriptions=this.subscriptions.filter((t=>t!==e)),e}findAll(e){return this.subscriptions.filter((t=>t.identifier===e))}reload(){return this.subscriptions.map((e=>this.subscribe(e)))}notifyAll(e,...t){return this.subscriptions.map((n=>this.notify(n,e,...t)))}notify(e,t,...n){let o;return o="string"==typeof e?this.findAll(e):[e],o.map((e=>"function"==typeof e[t]?e[t](...n):void 0))}subscribe(e){this.sendCommand(e,"subscribe")&&this.guarantor.guarantee(e)}confirmSubscription(e){t.log(`Subscription confirmed ${e}`),this.findAll(e).map((e=>this.guarantor.forget(e)))}sendCommand(e,t){const{identifier:n}=e;return this.consumer.send({command:t,identifier:n})}}class f{constructor(e){this._url=e,this.subscriptions=new p(this),this.connection=new d(this),this.subprotocols=[]}get url(){return function(e){"function"==typeof e&&(e=e());if(e&&!/^wss?:/i.test(e)){const t=document.createElement("a");return t.href=e,t.href=t.href,t.protocol=t.protocol.replace("http","ws"),t.href}return e}(this._url)}send(e){return this.connection.send(e)}connect(){return this.connection.open()}disconnect(){return this.connection.close({allowReconnect:!1})}ensureActiveConnection(){if(!this.connection.isActive())return this.connection.open()}addSubProtocol(e){this.subprotocols=[...this.subprotocols,e]}}var m=function(e=function(e){const t=document.head.querySelector(`meta[name='action-cable-${e}']`);if(t)return t.getAttribute("content")}("url")||r.default_mount_path){return new f(e)}("/hotwire-spark");function g(e){return e.replace(/-[a-z0-9]+\.(\w+)(\?.*)?$/,".$1")}function b(e,t){const n=new URL(e,window.location.origin);return Object.entries(t).forEach((e=>{let[t,o]=e;n.searchParams.set(t,o)})),n.toString()}function v(e){return b(e,{reload:Date.now()})}async function S(){let e=v(b(window.location.href,{hotwire_spark:"true"}));const t=await fetch(e,{headers:{Accept:"text/html"}});if(!t.ok)throw new Error(`${t.status} when fetching ${e}`);const n=await t.text();return(new DOMParser).parseFromString(n,"text/html")}var y=function(){let e=new Set,t={morphStyle:"outerHTML",callbacks:{beforeNodeAdded:a,afterNodeAdded:a,beforeNodeMorphed:a,afterNodeMorphed:a,beforeNodeRemoved:a,afterNodeRemoved:a,beforeAttributeUpdated:a},head:{style:"merge",shouldPreserve:function(e){return"true"===e.getAttribute("im-preserve")},shouldReAppend:function(e){return"true"===e.getAttribute("im-re-append")},shouldRemove:a,afterHeadMorphed:a}};function n(e,t,o){if(o.head.block){let i=e.querySelector("head"),r=t.querySelector("head");if(i&&r){let s=l(r,i,o);return void Promise.all(s).then((function(){n(e,t,Object.assign(o,{head:{block:!1,ignore:!0}}))}))}}if("innerHTML"===o.morphStyle)return r(t,e,o),e.children;if("outerHTML"===o.morphStyle||null==o.morphStyle){let n=function(e,t,n){let o;o=e.firstChild;let i=o,r=0;for(;o;){let e=m(o,t,n);e>r&&(i=o,r=e),o=o.nextSibling}return i}(t,e,o),r=n?.previousSibling,s=n?.nextSibling,c=i(e,n,o);return n?function(e,t,n){let o=[],i=[];for(;null!=e;)o.push(e),e=e.previousSibling;for(;o.length>0;){let e=o.pop();i.push(e),t.parentElement.insertBefore(e,t)}i.push(t);for(;null!=n;)o.push(n),i.push(n),n=n.nextSibling;for(;o.length>0;)t.parentElement.insertBefore(o.pop(),t.nextSibling);return i}(r,c,s):[]}throw"Do not understand how to morph style "+o.morphStyle}function o(e,t){return t.ignoreActiveValue&&e===document.activeElement}function i(e,t,n){if(!n.ignoreActive||e!==document.activeElement)return null==t?!1===n.callbacks.beforeNodeRemoved(e)?e:(e.remove(),n.callbacks.afterNodeRemoved(e),null):u(e,t)?(!1===n.callbacks.beforeNodeMorphed(e,t)||(e instanceof HTMLHeadElement&&n.head.ignore||(e instanceof HTMLHeadElement&&"morph"!==n.head.style?l(t,e,n):(!function(e,t,n){let i=e.nodeType;if(1===i){const o=e.attributes,i=t.attributes;for(const e of o)s(e.name,t,"update",n)||t.getAttribute(e.name)!==e.value&&t.setAttribute(e.name,e.value);for(let o=i.length-1;0<=o;o--){const r=i[o];s(r.name,t,"remove",n)||(e.hasAttribute(r.name)||t.removeAttribute(r.name))}}8!==i&&3!==i||t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue);o(t,n)||function(e,t,n){if(e instanceof HTMLInputElement&&t instanceof HTMLInputElement&&"file"!==e.type){let o=e.value,i=t.value;c(e,t,"checked",n),c(e,t,"disabled",n),e.hasAttribute("value")?o!==i&&(s("value",t,"update",n)||(t.setAttribute("value",o),t.value=o)):s("value",t,"remove",n)||(t.value="",t.removeAttribute("value"))}else if(e instanceof HTMLOptionElement)c(e,t,"selected",n);else if(e instanceof HTMLTextAreaElement&&t instanceof HTMLTextAreaElement){let o=e.value,i=t.value;if(s("value",t,"update",n))return;o!==i&&(t.value=o),t.firstChild&&t.firstChild.nodeValue!==o&&(t.firstChild.nodeValue=o)}}(e,t,n)}(t,e,n),o(e,n)||r(t,e,n))),n.callbacks.afterNodeMorphed(e,t)),e):!1===n.callbacks.beforeNodeRemoved(e)||!1===n.callbacks.beforeNodeAdded(t)?e:(e.parentElement.replaceChild(t,e),n.callbacks.afterNodeAdded(t),n.callbacks.afterNodeRemoved(e),t)}function r(e,t,n){let o,r=e.firstChild,s=t.firstChild;for(;r;){if(o=r,r=o.nextSibling,null==s){if(!1===n.callbacks.beforeNodeAdded(o))return;t.appendChild(o),n.callbacks.afterNodeAdded(o),S(n,o);continue}if(d(o,s,n)){i(s,o,n),s=s.nextSibling,S(n,o);continue}let c=p(e,t,o,s,n);if(c){s=h(s,c,n),i(c,o,n),S(n,o);continue}let l=f(e,t,o,s,n);if(l)s=h(s,l,n),i(l,o,n),S(n,o);else{if(!1===n.callbacks.beforeNodeAdded(o))return;t.insertBefore(o,s),n.callbacks.afterNodeAdded(o),S(n,o)}}for(;null!==s;){let e=s;s=s.nextSibling,g(e,n)}}function s(e,t,n,o){return!("value"!==e||!o.ignoreActiveValue||t!==document.activeElement)||!1===o.callbacks.beforeAttributeUpdated(e,t,n)}function c(e,t,n,o){if(e[n]!==t[n]){let i=s(n,t,"update",o);i||(t[n]=e[n]),e[n]?i||t.setAttribute(n,e[n]):s(n,t,"remove",o)||t.removeAttribute(n)}}function l(e,t,n){let o=[],i=[],r=[],s=[],c=n.head.style,l=new Map;for(const t of e.children)l.set(t.outerHTML,t);for(const e of t.children){let t=l.has(e.outerHTML),o=n.head.shouldReAppend(e),a=n.head.shouldPreserve(e);t||a?o?i.push(e):(l.delete(e.outerHTML),r.push(e)):"append"===c?o&&(i.push(e),s.push(e)):!1!==n.head.shouldRemove(e)&&i.push(e)}s.push(...l.values());let a=[];for(const e of s){let i=document.createRange().createContextualFragment(e.outerHTML).firstChild;if(!1!==n.callbacks.beforeNodeAdded(i)){if(i.href||i.src){let e=null,t=new Promise((function(t){e=t}));i.addEventListener("load",(function(){e()})),a.push(t)}t.appendChild(i),n.callbacks.afterNodeAdded(i),o.push(i)}}for(const e of i)!1!==n.callbacks.beforeNodeRemoved(e)&&(t.removeChild(e),n.callbacks.afterNodeRemoved(e));return n.head.afterHeadMorphed(t,{added:o,kept:r,removed:i}),a}function a(){}function d(e,t,n){return null!=e&&null!=t&&(e.nodeType===t.nodeType&&e.tagName===t.tagName&&(""!==e.id&&e.id===t.id||y(n,e,t)>0))}function u(e,t){return null!=e&&null!=t&&(e.nodeType===t.nodeType&&e.tagName===t.tagName)}function h(e,t,n){for(;e!==t;){let t=e;e=e.nextSibling,g(t,n)}return S(n,t),t.nextSibling}function p(e,t,n,o,i){let r=y(i,n,t);if(r>0){let t=o,s=0;for(;null!=t;){if(d(n,t,i))return t;if(s+=y(i,t,e),s>r)return null;t=t.nextSibling}}return null}function f(e,t,n,o,i){let r=o,s=n.nextSibling,c=0;for(;null!=r;){if(y(i,r,e)>0)return null;if(u(n,r))return r;if(u(s,r)&&(c++,s=s.nextSibling,c>=2))return null;r=r.nextSibling}return r}function m(e,t,n){return u(e,t)?.5+y(n,e,t):0}function g(e,t){S(t,e),!1!==t.callbacks.beforeNodeRemoved(e)&&(e.remove(),t.callbacks.afterNodeRemoved(e))}function b(e,t){return!e.deadIds.has(t)}function v(t,n,o){return(t.idMap.get(o)||e).has(n)}function S(t,n){let o=t.idMap.get(n)||e;for(const e of o)t.deadIds.add(e)}function y(t,n,o){let i=t.idMap.get(n)||e,r=0;for(const e of i)b(t,e)&&v(t,e,o)&&++r;return r}function w(e,t){let n=e.parentElement,o=e.querySelectorAll("[id]");for(const e of o){let o=e;for(;o!==n&&null!=o;){let n=t.get(o);null==n&&(n=new Set,t.set(o,n)),n.add(e.id),o=o.parentElement}}}function A(e,t){let n=new Map;return w(e,n),w(t,n),n}return{morph:function(e,o,i={}){e instanceof Document&&(e=e.documentElement),"string"==typeof o&&(o=function(e){let t=new DOMParser,n=e.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,"");if(n.match(/<\/html>/)||n.match(/<\/head>/)||n.match(/<\/body>/)){let o=t.parseFromString(e,"text/html");if(n.match(/<\/html>/))return o.generatedByIdiomorph=!0,o;{let e=o.firstChild;return e?(e.generatedByIdiomorph=!0,e):null}}{let n=t.parseFromString("<body><template>"+e+"</template></body>","text/html").body.querySelector("template").content;return n.generatedByIdiomorph=!0,n}}(o));let r=function(e){if(null==e){return document.createElement("div")}if(e.generatedByIdiomorph)return e;if(e instanceof Node){const t=document.createElement("div");return t.append(e),t}{const t=document.createElement("div");for(const n of[...e])t.append(n);return t}}(o),s=function(e,n,o){return o=function(e){let n={};return Object.assign(n,t),Object.assign(n,e),n.callbacks={},Object.assign(n.callbacks,t.callbacks),Object.assign(n.callbacks,e.callbacks),n.head={},Object.assign(n.head,t.head),Object.assign(n.head,e.head),n}(o),{target:e,newContent:n,config:o,morphStyle:o.morphStyle,ignoreActive:o.ignoreActive,ignoreActiveValue:o.ignoreActiveValue,idMap:A(e,n),deadIds:new Set,callbacks:o.callbacks,head:o.head}}(e,r,i);return n(e,r,s)},defaults:t}}();function w(){if(E.config.loggingEnabled){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];console.log("[hotwire_spark]",...t)}}class A{static async reload(e){return new A(e).reload()}static async reloadAll(){return Stimulus.controllers.forEach((e=>{Stimulus.unload(e.identifier),Stimulus.register(e.identifier,e.constructor)})),Promise.resolve()}constructor(e){this.changedPath=e,this.application=window.Stimulus}async reload(){w("Reload Stimulus controllers..."),this.application.stop();try{await this.#e()}catch(e){e instanceof k?this.#t():console.error("Error reloading controller",e)}this.application.start()}async#e(){const e=await this.#n(this.changedPath);await this.#o(this.#i,e)}async#n(e){const t=await fetch(`/spark/source_files/?path=${e}`);if(404===t.status)throw new k(`Source file not found: ${e}`);const n=await t.text(),o=new Blob([n],{type:"application/javascript"}),i=URL.createObjectURL(o),r=await import(i);return URL.revokeObjectURL(i),r}get#i(){return this.changedControllerIdentifier=this.changedControllerIdentifier||this.#r(this.changedPath),this.changedControllerIdentifier}#r(e){return e.replace(/^.*\//,"").replace("_controller","").replace(/\//g,"--").replace(/_/g,"-").replace(/\.js$/,"")}#t(){this.#s(this.#i)}#o(e,t){w("\tReloading controller",e),this.application.unload(e),this.application.register(e,t.default)}#s(e){w(`\tRemoving controller ${e}`),this.application.unload(e)}}class k extends Error{}class C{static async reload(){return(new C).reload()}async reload(){await this.#c(),await this.#l()}async#c(){w("Reload html with morph...");const e=await S();return this.#a(e.body),e}#a(e){y.morph(document.body,e)}async#l(){await A.reloadAll()}}class R{static async reload(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return new R(...t).reload()}constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:/./;this.filePattern=e}async reload(){w("Reload css..."),await Promise.all(await this.#d())}async#d(){return(await this.#u()).map((e=>this.#h(e)))}async#u(){const e=await S();return Array.from(e.head.querySelectorAll("link[rel='stylesheet']"))}#h(e){return this.#p(e)?this.#f(e):Promise.resolve()}#p(e){return this.filePattern.test(e.getAttribute("href"))}async#f(e){return new Promise((t=>{const n=e.getAttribute("href"),o=this.#m(e)||this.#g(e);o.setAttribute("href",v(e.getAttribute("href"))),o.onload=()=>{w(`\t${n}`),t()}}))}#m(e){return this.#b.find((t=>g(e.href)===g(t.href)))}get#b(){return Array.from(document.querySelectorAll("link[rel='stylesheet']"))}#g(e){return document.head.append(e),e}}class M{static async reload(){return(new M).reload()}async reload(){await this.#c()}async#c(){w("Reload html with Turbo..."),this.#v(),await this.#S()}#v(){document.addEventListener("turbo:before-render",(()=>{Turbo.navigator.currentVisit.scrolled=!0}),{once:!0})}#S(){return new Promise((e=>{document.addEventListener("turbo:load",(()=>e(document)),{once:!0}),window.Turbo.visit(window.location)}))}}m.subscriptions.create({channel:"Hotwire::Spark::Channel"},{connected(){document.body.setAttribute("data-hotwire-spark-ready","")},async received(e){try{await this.dispatch(e)}catch(t){console.log(`Error on ${e.action}`,t)}},dispatch(e){let{action:t,path:n}=e;switch(t){case"reload_html":return this.reloadHtml();case"reload_css":return this.reloadCss(n);case"reload_stimulus":return this.reloadStimulus(n);default:throw new Error(`Unknown action: ${t}`)}},reloadHtml:()=>("morph"==HotwireSpark.config.htmlReloadMethod?C:M).reload(),reloadCss(e){const t=function(e){return e.split("/").pop().split(".")[0]}(e);return R.reload(new RegExp(t))},reloadStimulus:e=>A.reload(e)});const E={config:{loggingEnabled:!1,htmlReloadMethod:"morph"}},L={loggingEnabled:"logging",htmlReloadMethod:"html-reload-method"};return document.addEventListener("DOMContentLoaded",(function(){Object.entries(L).forEach((e=>{let[t,n]=e;var o;E.config[t]=(o=n,document.querySelector(`meta[name="hotwire-spark:${o}"]`)?.content)}))})),E}();
|
2
2
|
//# sourceMappingURL=hotwire_spark.min.js.map
|