summaryrefslogtreecommitdiff
path: root/public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-06-26 13:40:55 +0200
committererdgeist <erdgeist@erdgeist.org>2026-06-26 13:40:55 +0200
commit811bb04649365b0faaa00b1e0810bb101a4d19b1 (patch)
tree581d52faa6e1bf21a1cdfd4da7127a0f95786bad /public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js
parenta1ddc25da0d2aa79a4d9216ef7792f26233bd38e (diff)
Stage 5 click-testing fixes
- Fix link_to :method → button_to for all PUT/DELETE actions - Add button_to CSS reset to admin.css for visual consistency - Fix admin layout: replace broken jquery/jquery_ujs pipeline refs with admin_bundle via sprockets; add sprockets-rails, jquery-ui-rails gems - Add app/assets/javascripts/admin_bundle.js pipeline manifest - Fix event_information helper: use safe_join to avoid double-escaping - Fix nodes_helper: to_s(:db) → to_fs(:db) for event times - Fix revisions view: eliminate nested forms; diff button uses vanilla JS to collect radio button values before POST - Fix config/environments/development.rb and test.rb: cache_classes → enable_reloading - Add routing_filter_rails71_patch.rb version guard - Move LockedByAnotherUser to own file for Zeitwerk autoloading - Fix Globalize fallbacks via config.i18n.fallbacks in application.rb
Diffstat (limited to 'public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js')
-rw-r--r--public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js510
1 files changed, 510 insertions, 0 deletions
diff --git a/public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js b/public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js
new file mode 100644
index 00000000..5fc99433
--- /dev/null
+++ b/public/assets/actioncable-1a239ff96644bc8fd626f87a74175b4ee7eafd201f5034034a433c22d8c4dc3e.js
@@ -0,0 +1,510 @@
1(function(global, factory) {
2 typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
3 factory(global.ActionCable = {}));
4})(this, (function(exports) {
5 "use strict";
6 var adapters = {
7 logger: typeof console !== "undefined" ? console : undefined,
8 WebSocket: typeof WebSocket !== "undefined" ? WebSocket : undefined
9 };
10 var logger = {
11 log(...messages) {
12 if (this.enabled) {
13 messages.push(Date.now());
14 adapters.logger.log("[ActionCable]", ...messages);
15 }
16 }
17 };
18 const now = () => (new Date).getTime();
19 const secondsSince = time => (now() - time) / 1e3;
20 class ConnectionMonitor {
21 constructor(connection) {
22 this.visibilityDidChange = this.visibilityDidChange.bind(this);
23 this.connection = connection;
24 this.reconnectAttempts = 0;
25 }
26 start() {
27 if (!this.isRunning()) {
28 this.startedAt = now();
29 delete this.stoppedAt;
30 this.startPolling();
31 addEventListener("visibilitychange", this.visibilityDidChange);
32 logger.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`);
33 }
34 }
35 stop() {
36 if (this.isRunning()) {
37 this.stoppedAt = now();
38 this.stopPolling();
39 removeEventListener("visibilitychange", this.visibilityDidChange);
40 logger.log("ConnectionMonitor stopped");
41 }
42 }
43 isRunning() {
44 return this.startedAt && !this.stoppedAt;
45 }
46 recordMessage() {
47 this.pingedAt = now();
48 }
49 recordConnect() {
50 this.reconnectAttempts = 0;
51 delete this.disconnectedAt;
52 logger.log("ConnectionMonitor recorded connect");
53 }
54 recordDisconnect() {
55 this.disconnectedAt = now();
56 logger.log("ConnectionMonitor recorded disconnect");
57 }
58 startPolling() {
59 this.stopPolling();
60 this.poll();
61 }
62 stopPolling() {
63 clearTimeout(this.pollTimeout);
64 }
65 poll() {
66 this.pollTimeout = setTimeout((() => {
67 this.reconnectIfStale();
68 this.poll();
69 }), this.getPollInterval());
70 }
71 getPollInterval() {
72 const {staleThreshold: staleThreshold, reconnectionBackoffRate: reconnectionBackoffRate} = this.constructor;
73 const backoff = Math.pow(1 + reconnectionBackoffRate, Math.min(this.reconnectAttempts, 10));
74 const jitterMax = this.reconnectAttempts === 0 ? 1 : reconnectionBackoffRate;
75 const jitter = jitterMax * Math.random();
76 return staleThreshold * 1e3 * backoff * (1 + jitter);
77 }
78 reconnectIfStale() {
79 if (this.connectionIsStale()) {
80 logger.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${secondsSince(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`);
81 this.reconnectAttempts++;
82 if (this.disconnectedRecently()) {
83 logger.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${secondsSince(this.disconnectedAt)} s`);
84 } else {
85 logger.log("ConnectionMonitor reopening");
86 this.connection.reopen();
87 }
88 }
89 }
90 get refreshedAt() {
91 return this.pingedAt ? this.pingedAt : this.startedAt;
92 }
93 connectionIsStale() {
94 return secondsSince(this.refreshedAt) > this.constructor.staleThreshold;
95 }
96 disconnectedRecently() {
97 return this.disconnectedAt && secondsSince(this.disconnectedAt) < this.constructor.staleThreshold;
98 }
99 visibilityDidChange() {
100 if (document.visibilityState === "visible") {
101 setTimeout((() => {
102 if (this.connectionIsStale() || !this.connection.isOpen()) {
103 logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`);
104 this.connection.reopen();
105 }
106 }), 200);
107 }
108 }
109 }
110 ConnectionMonitor.staleThreshold = 6;
111 ConnectionMonitor.reconnectionBackoffRate = .15;
112 var INTERNAL = {
113 message_types: {
114 welcome: "welcome",
115 disconnect: "disconnect",
116 ping: "ping",
117 confirmation: "confirm_subscription",
118 rejection: "reject_subscription"
119 },
120 disconnect_reasons: {
121 unauthorized: "unauthorized",
122 invalid_request: "invalid_request",
123 server_restart: "server_restart",
124 remote: "remote"
125 },
126 default_mount_path: "/cable",
127 protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
128 };
129 const {message_types: message_types, protocols: protocols} = INTERNAL;
130 const supportedProtocols = protocols.slice(0, protocols.length - 1);
131 const indexOf = [].indexOf;
132 class Connection {
133 constructor(consumer) {
134 this.open = this.open.bind(this);
135 this.consumer = consumer;
136 this.subscriptions = this.consumer.subscriptions;
137 this.monitor = new ConnectionMonitor(this);
138 this.disconnected = true;
139 }
140 send(data) {
141 if (this.isOpen()) {
142 this.webSocket.send(JSON.stringify(data));
143 return true;
144 } else {
145 return false;
146 }
147 }
148 open() {
149 if (this.isActive()) {
150 logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
151 return false;
152 } else {
153 const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
154 logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
155 if (this.webSocket) {
156 this.uninstallEventHandlers();
157 }
158 this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
159 this.installEventHandlers();
160 this.monitor.start();
161 return true;
162 }
163 }
164 close({allowReconnect: allowReconnect} = {
165 allowReconnect: true
166 }) {
167 if (!allowReconnect) {
168 this.monitor.stop();
169 }
170 if (this.isOpen()) {
171 return this.webSocket.close();
172 }
173 }
174 reopen() {
175 logger.log(`Reopening WebSocket, current state is ${this.getState()}`);
176 if (this.isActive()) {
177 try {
178 return this.close();
179 } catch (error) {
180 logger.log("Failed to reopen WebSocket", error);
181 } finally {
182 logger.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`);
183 setTimeout(this.open, this.constructor.reopenDelay);
184 }
185 } else {
186 return this.open();
187 }
188 }
189 getProtocol() {
190 if (this.webSocket) {
191 return this.webSocket.protocol;
192 }
193 }
194 isOpen() {
195 return this.isState("open");
196 }
197 isActive() {
198 return this.isState("open", "connecting");
199 }
200 triedToReconnect() {
201 return this.monitor.reconnectAttempts > 0;
202 }
203 isProtocolSupported() {
204 return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
205 }
206 isState(...states) {
207 return indexOf.call(states, this.getState()) >= 0;
208 }
209 getState() {
210 if (this.webSocket) {
211 for (let state in adapters.WebSocket) {
212 if (adapters.WebSocket[state] === this.webSocket.readyState) {
213 return state.toLowerCase();
214 }
215 }
216 }
217 return null;
218 }
219 installEventHandlers() {
220 for (let eventName in this.events) {
221 const handler = this.events[eventName].bind(this);
222 this.webSocket[`on${eventName}`] = handler;
223 }
224 }
225 uninstallEventHandlers() {
226 for (let eventName in this.events) {
227 this.webSocket[`on${eventName}`] = function() {};
228 }
229 }
230 }
231 Connection.reopenDelay = 500;
232 Connection.prototype.events = {
233 message(event) {
234 if (!this.isProtocolSupported()) {
235 return;
236 }
237 const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
238 this.monitor.recordMessage();
239 switch (type) {
240 case message_types.welcome:
241 if (this.triedToReconnect()) {
242 this.reconnectAttempted = true;
243 }
244 this.monitor.recordConnect();
245 return this.subscriptions.reload();
246
247 case message_types.disconnect:
248 logger.log(`Disconnecting. Reason: ${reason}`);
249 return this.close({
250 allowReconnect: reconnect
251 });
252
253 case message_types.ping:
254 return null;
255
256 case message_types.confirmation:
257 this.subscriptions.confirmSubscription(identifier);
258 if (this.reconnectAttempted) {
259 this.reconnectAttempted = false;
260 return this.subscriptions.notify(identifier, "connected", {
261 reconnected: true
262 });
263 } else {
264 return this.subscriptions.notify(identifier, "connected", {
265 reconnected: false
266 });
267 }
268
269 case message_types.rejection:
270 return this.subscriptions.reject(identifier);
271
272 default:
273 return this.subscriptions.notify(identifier, "received", message);
274 }
275 },
276 open() {
277 logger.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`);
278 this.disconnected = false;
279 if (!this.isProtocolSupported()) {
280 logger.log("Protocol is unsupported. Stopping monitor and disconnecting.");
281 return this.close({
282 allowReconnect: false
283 });
284 }
285 },
286 close(event) {
287 logger.log("WebSocket onclose event");
288 if (this.disconnected) {
289 return;
290 }
291 this.disconnected = true;
292 this.monitor.recordDisconnect();
293 return this.subscriptions.notifyAll("disconnected", {
294 willAttemptReconnect: this.monitor.isRunning()
295 });
296 },
297 error() {
298 logger.log("WebSocket onerror event");
299 }
300 };
301 const extend = function(object, properties) {
302 if (properties != null) {
303 for (let key in properties) {
304 const value = properties[key];
305 object[key] = value;
306 }
307 }
308 return object;
309 };
310 class Subscription {
311 constructor(consumer, params = {}, mixin) {
312 this.consumer = consumer;
313 this.identifier = JSON.stringify(params);
314 extend(this, mixin);
315 }
316 perform(action, data = {}) {
317 data.action = action;
318 return this.send(data);
319 }
320 send(data) {
321 return this.consumer.send({
322 command: "message",
323 identifier: this.identifier,
324 data: JSON.stringify(data)
325 });
326 }
327 unsubscribe() {
328 return this.consumer.subscriptions.remove(this);
329 }
330 }
331 class SubscriptionGuarantor {
332 constructor(subscriptions) {
333 this.subscriptions = subscriptions;
334 this.pendingSubscriptions = [];
335 }
336 guarantee(subscription) {
337 if (this.pendingSubscriptions.indexOf(subscription) == -1) {
338 logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`);
339 this.pendingSubscriptions.push(subscription);
340 } else {
341 logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`);
342 }
343 this.startGuaranteeing();
344 }
345 forget(subscription) {
346 logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`);
347 this.pendingSubscriptions = this.pendingSubscriptions.filter((s => s !== subscription));
348 }
349 startGuaranteeing() {
350 this.stopGuaranteeing();
351 this.retrySubscribing();
352 }
353 stopGuaranteeing() {
354 clearTimeout(this.retryTimeout);
355 }
356 retrySubscribing() {
357 this.retryTimeout = setTimeout((() => {
358 if (this.subscriptions && typeof this.subscriptions.subscribe === "function") {
359 this.pendingSubscriptions.map((subscription => {
360 logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`);
361 this.subscriptions.subscribe(subscription);
362 }));
363 }
364 }), 500);
365 }
366 }
367 class Subscriptions {
368 constructor(consumer) {
369 this.consumer = consumer;
370 this.guarantor = new SubscriptionGuarantor(this);
371 this.subscriptions = [];
372 }
373 create(channelName, mixin) {
374 const channel = channelName;
375 const params = typeof channel === "object" ? channel : {
376 channel: channel
377 };
378 const subscription = new Subscription(this.consumer, params, mixin);
379 return this.add(subscription);
380 }
381 add(subscription) {
382 this.subscriptions.push(subscription);
383 this.consumer.ensureActiveConnection();
384 this.notify(subscription, "initialized");
385 this.subscribe(subscription);
386 return subscription;
387 }
388 remove(subscription) {
389 this.forget(subscription);
390 if (!this.findAll(subscription.identifier).length) {
391 this.sendCommand(subscription, "unsubscribe");
392 }
393 return subscription;
394 }
395 reject(identifier) {
396 return this.findAll(identifier).map((subscription => {
397 this.forget(subscription);
398 this.notify(subscription, "rejected");
399 return subscription;
400 }));
401 }
402 forget(subscription) {
403 this.guarantor.forget(subscription);
404 this.subscriptions = this.subscriptions.filter((s => s !== subscription));
405 return subscription;
406 }
407 findAll(identifier) {
408 return this.subscriptions.filter((s => s.identifier === identifier));
409 }
410 reload() {
411 return this.subscriptions.map((subscription => this.subscribe(subscription)));
412 }
413 notifyAll(callbackName, ...args) {
414 return this.subscriptions.map((subscription => this.notify(subscription, callbackName, ...args)));
415 }
416 notify(subscription, callbackName, ...args) {
417 let subscriptions;
418 if (typeof subscription === "string") {
419 subscriptions = this.findAll(subscription);
420 } else {
421 subscriptions = [ subscription ];
422 }
423 return subscriptions.map((subscription => typeof subscription[callbackName] === "function" ? subscription[callbackName](...args) : undefined));
424 }
425 subscribe(subscription) {
426 if (this.sendCommand(subscription, "subscribe")) {
427 this.guarantor.guarantee(subscription);
428 }
429 }
430 confirmSubscription(identifier) {
431 logger.log(`Subscription confirmed ${identifier}`);
432 this.findAll(identifier).map((subscription => this.guarantor.forget(subscription)));
433 }
434 sendCommand(subscription, command) {
435 const {identifier: identifier} = subscription;
436 return this.consumer.send({
437 command: command,
438 identifier: identifier
439 });
440 }
441 }
442 class Consumer {
443 constructor(url) {
444 this._url = url;
445 this.subscriptions = new Subscriptions(this);
446 this.connection = new Connection(this);
447 this.subprotocols = [];
448 }
449 get url() {
450 return createWebSocketURL(this._url);
451 }
452 send(data) {
453 return this.connection.send(data);
454 }
455 connect() {
456 return this.connection.open();
457 }
458 disconnect() {
459 return this.connection.close({
460 allowReconnect: false
461 });
462 }
463 ensureActiveConnection() {
464 if (!this.connection.isActive()) {
465 return this.connection.open();
466 }
467 }
468 addSubProtocol(subprotocol) {
469 this.subprotocols = [ ...this.subprotocols, subprotocol ];
470 }
471 }
472 function createWebSocketURL(url) {
473 if (typeof url === "function") {
474 url = url();
475 }
476 if (url && !/^wss?:/i.test(url)) {
477 const a = document.createElement("a");
478 a.href = url;
479 a.href = a.href;
480 a.protocol = a.protocol.replace("http", "ws");
481 return a.href;
482 } else {
483 return url;
484 }
485 }
486 function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) {
487 return new Consumer(url);
488 }
489 function getConfig(name) {
490 const element = document.head.querySelector(`meta[name='action-cable-${name}']`);
491 if (element) {
492 return element.getAttribute("content");
493 }
494 }
495 exports.Connection = Connection;
496 exports.ConnectionMonitor = ConnectionMonitor;
497 exports.Consumer = Consumer;
498 exports.INTERNAL = INTERNAL;
499 exports.Subscription = Subscription;
500 exports.SubscriptionGuarantor = SubscriptionGuarantor;
501 exports.Subscriptions = Subscriptions;
502 exports.adapters = adapters;
503 exports.createConsumer = createConsumer;
504 exports.createWebSocketURL = createWebSocketURL;
505 exports.getConfig = getConfig;
506 exports.logger = logger;
507 Object.defineProperty(exports, "__esModule", {
508 value: true
509 });
510}));