cable_ready 5.0.0.pre8 → 5.0.0.pre9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,910 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("morphdom")) : typeof define === "function" && define.amd ? define([ "exports", "morphdom" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
3
+ factory(global.CableReady = {}, global.morphdom));
4
+ })(this, (function(exports, morphdom) {
5
+ "use strict";
6
+ function _interopDefaultLegacy(e) {
7
+ return e && typeof e === "object" && "default" in e ? e : {
8
+ default: e
9
+ };
10
+ }
11
+ var morphdom__default = _interopDefaultLegacy(morphdom);
12
+ var name = "cable_ready";
13
+ var version = "5.0.0-pre9";
14
+ var description = "CableReady helps you create great real-time user experiences by making it simple to trigger client-side DOM changes from server-side Ruby.";
15
+ var keywords = [ "ruby", "rails", "websockets", "actioncable", "cable", "ssr", "stimulus_reflex", "client-side", "dom" ];
16
+ var homepage = "https://cableready.stimulusreflex.com/";
17
+ var bugs = {
18
+ url: "https://github.com/stimulusreflex/cable_ready/issues"
19
+ };
20
+ var repository = {
21
+ type: "git",
22
+ url: "git+https://github.com:stimulusreflex/cable_ready.git"
23
+ };
24
+ var license = "MIT";
25
+ var author = "Nathan Hopkins <natehop@gmail.com>";
26
+ var main = "./dist/cable_ready.umd.min.js";
27
+ var module = "./dist/cable_ready.min.js";
28
+ var files = [ "dist/*", "javascript/*" ];
29
+ var scripts = {
30
+ lint: "yarn run prettier-standard:check",
31
+ format: "yarn run prettier-standard:format",
32
+ "prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
33
+ "prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
34
+ build: "yarn rollup -c",
35
+ watch: "yarn rollup -wc"
36
+ };
37
+ var dependencies = {
38
+ morphdom: "^2.6.1"
39
+ };
40
+ var devDependencies = {
41
+ "@rollup/plugin-commonjs": "^21.0.3",
42
+ "@rollup/plugin-json": "^4.1.0",
43
+ "@rollup/plugin-node-resolve": "^13.1.3",
44
+ "prettier-standard": "^16.4.1",
45
+ rollup: "^2.70.1",
46
+ "rollup-plugin-terser": "^7.0.2"
47
+ };
48
+ var packageInfo = {
49
+ name: name,
50
+ version: version,
51
+ description: description,
52
+ keywords: keywords,
53
+ homepage: homepage,
54
+ bugs: bugs,
55
+ repository: repository,
56
+ license: license,
57
+ author: author,
58
+ main: main,
59
+ module: module,
60
+ files: files,
61
+ scripts: scripts,
62
+ dependencies: dependencies,
63
+ devDependencies: devDependencies
64
+ };
65
+ const inputTags = {
66
+ INPUT: true,
67
+ TEXTAREA: true,
68
+ SELECT: true
69
+ };
70
+ const mutableTags = {
71
+ INPUT: true,
72
+ TEXTAREA: true,
73
+ OPTION: true
74
+ };
75
+ const textInputTypes = {
76
+ "datetime-local": true,
77
+ "select-multiple": true,
78
+ "select-one": true,
79
+ color: true,
80
+ date: true,
81
+ datetime: true,
82
+ email: true,
83
+ month: true,
84
+ number: true,
85
+ password: true,
86
+ range: true,
87
+ search: true,
88
+ tel: true,
89
+ text: true,
90
+ textarea: true,
91
+ time: true,
92
+ url: true,
93
+ week: true
94
+ };
95
+ let activeElement;
96
+ var ActiveElement = {
97
+ get element() {
98
+ return activeElement;
99
+ },
100
+ set(element) {
101
+ activeElement = element;
102
+ }
103
+ };
104
+ const isTextInput = element => inputTags[element.tagName] && textInputTypes[element.type];
105
+ const assignFocus = selector => {
106
+ const element = selector && selector.nodeType === Node.ELEMENT_NODE ? selector : document.querySelector(selector);
107
+ const focusElement = element || ActiveElement.element;
108
+ if (focusElement && focusElement.focus) focusElement.focus();
109
+ };
110
+ const dispatch = (element, name, detail = {}) => {
111
+ const init = {
112
+ bubbles: true,
113
+ cancelable: true,
114
+ detail: detail
115
+ };
116
+ const evt = new CustomEvent(name, init);
117
+ element.dispatchEvent(evt);
118
+ if (window.jQuery) window.jQuery(element).trigger(name, detail);
119
+ };
120
+ const xpathToElement = xpath => document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
121
+ const getClassNames = names => Array(names).flat();
122
+ const processElements = (operation, callback) => {
123
+ Array.from(operation.selectAll ? operation.element : [ operation.element ]).forEach(callback);
124
+ };
125
+ const kebabize = str => str.split("").map(((letter, idx) => letter.toUpperCase() === letter ? `${idx !== 0 ? "-" : ""}${letter.toLowerCase()}` : letter)).join("");
126
+ const operate = (operation, callback) => {
127
+ if (!operation.cancel) {
128
+ operation.delay ? setTimeout(callback, operation.delay) : callback();
129
+ return true;
130
+ }
131
+ return false;
132
+ };
133
+ const before = (target, operation) => dispatch(target, `cable-ready:before-${kebabize(operation.operation)}`, operation);
134
+ const after = (target, operation) => dispatch(target, `cable-ready:after-${kebabize(operation.operation)}`, operation);
135
+ function debounce(func, timeout) {
136
+ let timer;
137
+ return (...args) => {
138
+ clearTimeout(timer);
139
+ timer = setTimeout((() => func.apply(this, args)), timeout);
140
+ };
141
+ }
142
+ function handleErrors(response) {
143
+ if (!response.ok) throw Error(response.statusText);
144
+ return response;
145
+ }
146
+ async function graciouslyFetch(url, additionalHeaders) {
147
+ try {
148
+ const response = await fetch(url, {
149
+ headers: {
150
+ "X-REQUESTED-WITH": "XmlHttpRequest",
151
+ ...additionalHeaders
152
+ }
153
+ });
154
+ if (response == undefined) return;
155
+ handleErrors(response);
156
+ return response;
157
+ } catch (e) {
158
+ console.error(`Could not fetch ${url}`);
159
+ }
160
+ }
161
+ var utils = Object.freeze({
162
+ __proto__: null,
163
+ isTextInput: isTextInput,
164
+ assignFocus: assignFocus,
165
+ dispatch: dispatch,
166
+ xpathToElement: xpathToElement,
167
+ getClassNames: getClassNames,
168
+ processElements: processElements,
169
+ operate: operate,
170
+ before: before,
171
+ after: after,
172
+ debounce: debounce,
173
+ handleErrors: handleErrors,
174
+ graciouslyFetch: graciouslyFetch,
175
+ kebabize: kebabize
176
+ });
177
+ const shouldMorph = operation => (fromEl, toEl) => !shouldMorphCallbacks.map((callback => typeof callback === "function" ? callback(operation, fromEl, toEl) : true)).includes(false);
178
+ const didMorph = operation => el => {
179
+ didMorphCallbacks.forEach((callback => {
180
+ if (typeof callback === "function") callback(operation, el);
181
+ }));
182
+ };
183
+ const verifyNotMutable = (detail, fromEl, toEl) => {
184
+ if (!mutableTags[fromEl.tagName] && fromEl.isEqualNode(toEl)) return false;
185
+ return true;
186
+ };
187
+ const verifyNotContentEditable = (detail, fromEl, toEl) => {
188
+ if (fromEl === ActiveElement.element && fromEl.isContentEditable) return false;
189
+ return true;
190
+ };
191
+ const verifyNotPermanent = (detail, fromEl, toEl) => {
192
+ const {permanentAttributeName: permanentAttributeName} = detail;
193
+ if (!permanentAttributeName) return true;
194
+ const permanent = fromEl.closest(`[${permanentAttributeName}]`);
195
+ if (!permanent && fromEl === ActiveElement.element && isTextInput(fromEl)) {
196
+ const ignore = {
197
+ value: true
198
+ };
199
+ Array.from(toEl.attributes).forEach((attribute => {
200
+ if (!ignore[attribute.name]) fromEl.setAttribute(attribute.name, attribute.value);
201
+ }));
202
+ return false;
203
+ }
204
+ return !permanent;
205
+ };
206
+ const shouldMorphCallbacks = [ verifyNotMutable, verifyNotPermanent, verifyNotContentEditable ];
207
+ const didMorphCallbacks = [];
208
+ var morph_callbacks = Object.freeze({
209
+ __proto__: null,
210
+ shouldMorphCallbacks: shouldMorphCallbacks,
211
+ didMorphCallbacks: didMorphCallbacks,
212
+ shouldMorph: shouldMorph,
213
+ didMorph: didMorph,
214
+ verifyNotMutable: verifyNotMutable,
215
+ verifyNotContentEditable: verifyNotContentEditable,
216
+ verifyNotPermanent: verifyNotPermanent
217
+ });
218
+ var Operations = {
219
+ append: operation => {
220
+ processElements(operation, (element => {
221
+ before(element, operation);
222
+ operate(operation, (() => {
223
+ const {html: html, focusSelector: focusSelector} = operation;
224
+ element.insertAdjacentHTML("beforeend", html || "");
225
+ assignFocus(focusSelector);
226
+ }));
227
+ after(element, operation);
228
+ }));
229
+ },
230
+ graft: operation => {
231
+ processElements(operation, (element => {
232
+ before(element, operation);
233
+ operate(operation, (() => {
234
+ const {parent: parent, focusSelector: focusSelector} = operation;
235
+ const parentElement = document.querySelector(parent);
236
+ if (parentElement) {
237
+ parentElement.appendChild(element);
238
+ assignFocus(focusSelector);
239
+ }
240
+ }));
241
+ after(element, operation);
242
+ }));
243
+ },
244
+ innerHtml: operation => {
245
+ processElements(operation, (element => {
246
+ before(element, operation);
247
+ operate(operation, (() => {
248
+ const {html: html, focusSelector: focusSelector} = operation;
249
+ element.innerHTML = html || "";
250
+ assignFocus(focusSelector);
251
+ }));
252
+ after(element, operation);
253
+ }));
254
+ },
255
+ insertAdjacentHtml: operation => {
256
+ processElements(operation, (element => {
257
+ before(element, operation);
258
+ operate(operation, (() => {
259
+ const {html: html, position: position, focusSelector: focusSelector} = operation;
260
+ element.insertAdjacentHTML(position || "beforeend", html || "");
261
+ assignFocus(focusSelector);
262
+ }));
263
+ after(element, operation);
264
+ }));
265
+ },
266
+ insertAdjacentText: operation => {
267
+ processElements(operation, (element => {
268
+ before(element, operation);
269
+ operate(operation, (() => {
270
+ const {text: text, position: position, focusSelector: focusSelector} = operation;
271
+ element.insertAdjacentText(position || "beforeend", text || "");
272
+ assignFocus(focusSelector);
273
+ }));
274
+ after(element, operation);
275
+ }));
276
+ },
277
+ morph: operation => {
278
+ processElements(operation, (element => {
279
+ const {html: html} = operation;
280
+ const template = document.createElement("template");
281
+ template.innerHTML = String(html).trim();
282
+ operation.content = template.content;
283
+ const parent = element.parentElement;
284
+ const ordinal = Array.from(parent.children).indexOf(element);
285
+ before(element, operation);
286
+ operate(operation, (() => {
287
+ const {childrenOnly: childrenOnly, focusSelector: focusSelector} = operation;
288
+ morphdom__default["default"](element, childrenOnly ? template.content : template.innerHTML, {
289
+ childrenOnly: !!childrenOnly,
290
+ onBeforeElUpdated: shouldMorph(operation),
291
+ onElUpdated: didMorph(operation)
292
+ });
293
+ assignFocus(focusSelector);
294
+ }));
295
+ after(parent.children[ordinal], operation);
296
+ }));
297
+ },
298
+ outerHtml: operation => {
299
+ processElements(operation, (element => {
300
+ const parent = element.parentElement;
301
+ const ordinal = Array.from(parent.children).indexOf(element);
302
+ before(element, operation);
303
+ operate(operation, (() => {
304
+ const {html: html, focusSelector: focusSelector} = operation;
305
+ element.outerHTML = html || "";
306
+ assignFocus(focusSelector);
307
+ }));
308
+ after(parent.children[ordinal], operation);
309
+ }));
310
+ },
311
+ prepend: operation => {
312
+ processElements(operation, (element => {
313
+ before(element, operation);
314
+ operate(operation, (() => {
315
+ const {html: html, focusSelector: focusSelector} = operation;
316
+ element.insertAdjacentHTML("afterbegin", html || "");
317
+ assignFocus(focusSelector);
318
+ }));
319
+ after(element, operation);
320
+ }));
321
+ },
322
+ remove: operation => {
323
+ processElements(operation, (element => {
324
+ before(element, operation);
325
+ operate(operation, (() => {
326
+ const {focusSelector: focusSelector} = operation;
327
+ element.remove();
328
+ assignFocus(focusSelector);
329
+ }));
330
+ after(document, operation);
331
+ }));
332
+ },
333
+ replace: operation => {
334
+ processElements(operation, (element => {
335
+ const parent = element.parentElement;
336
+ const ordinal = Array.from(parent.children).indexOf(element);
337
+ before(element, operation);
338
+ operate(operation, (() => {
339
+ const {html: html, focusSelector: focusSelector} = operation;
340
+ element.outerHTML = html || "";
341
+ assignFocus(focusSelector);
342
+ }));
343
+ after(parent.children[ordinal], operation);
344
+ }));
345
+ },
346
+ textContent: operation => {
347
+ processElements(operation, (element => {
348
+ before(element, operation);
349
+ operate(operation, (() => {
350
+ const {text: text, focusSelector: focusSelector} = operation;
351
+ element.textContent = text != null ? text : "";
352
+ assignFocus(focusSelector);
353
+ }));
354
+ after(element, operation);
355
+ }));
356
+ },
357
+ addCssClass: operation => {
358
+ processElements(operation, (element => {
359
+ before(element, operation);
360
+ operate(operation, (() => {
361
+ const {name: name} = operation;
362
+ element.classList.add(...getClassNames(name || ""));
363
+ }));
364
+ after(element, operation);
365
+ }));
366
+ },
367
+ removeAttribute: operation => {
368
+ processElements(operation, (element => {
369
+ before(element, operation);
370
+ operate(operation, (() => {
371
+ const {name: name} = operation;
372
+ element.removeAttribute(name);
373
+ }));
374
+ after(element, operation);
375
+ }));
376
+ },
377
+ removeCssClass: operation => {
378
+ processElements(operation, (element => {
379
+ before(element, operation);
380
+ operate(operation, (() => {
381
+ const {name: name} = operation;
382
+ element.classList.remove(...getClassNames(name));
383
+ }));
384
+ after(element, operation);
385
+ }));
386
+ },
387
+ setAttribute: operation => {
388
+ processElements(operation, (element => {
389
+ before(element, operation);
390
+ operate(operation, (() => {
391
+ const {name: name, value: value} = operation;
392
+ element.setAttribute(name, value || "");
393
+ }));
394
+ after(element, operation);
395
+ }));
396
+ },
397
+ setDatasetProperty: operation => {
398
+ processElements(operation, (element => {
399
+ before(element, operation);
400
+ operate(operation, (() => {
401
+ const {name: name, value: value} = operation;
402
+ element.dataset[name] = value || "";
403
+ }));
404
+ after(element, operation);
405
+ }));
406
+ },
407
+ setProperty: operation => {
408
+ processElements(operation, (element => {
409
+ before(element, operation);
410
+ operate(operation, (() => {
411
+ const {name: name, value: value} = operation;
412
+ if (name in element) element[name] = value || "";
413
+ }));
414
+ after(element, operation);
415
+ }));
416
+ },
417
+ setStyle: operation => {
418
+ processElements(operation, (element => {
419
+ before(element, operation);
420
+ operate(operation, (() => {
421
+ const {name: name, value: value} = operation;
422
+ element.style[name] = value || "";
423
+ }));
424
+ after(element, operation);
425
+ }));
426
+ },
427
+ setStyles: operation => {
428
+ processElements(operation, (element => {
429
+ before(element, operation);
430
+ operate(operation, (() => {
431
+ const {styles: styles} = operation;
432
+ for (let [name, value] of Object.entries(styles)) element.style[name] = value || "";
433
+ }));
434
+ after(element, operation);
435
+ }));
436
+ },
437
+ setValue: operation => {
438
+ processElements(operation, (element => {
439
+ before(element, operation);
440
+ operate(operation, (() => {
441
+ const {value: value} = operation;
442
+ element.value = value || "";
443
+ }));
444
+ after(element, operation);
445
+ }));
446
+ },
447
+ dispatchEvent: operation => {
448
+ processElements(operation, (element => {
449
+ before(element, operation);
450
+ operate(operation, (() => {
451
+ const {name: name, detail: detail} = operation;
452
+ dispatch(element, name, detail);
453
+ }));
454
+ after(element, operation);
455
+ }));
456
+ },
457
+ setMeta: operation => {
458
+ before(document, operation);
459
+ operate(operation, (() => {
460
+ const {name: name, content: content} = operation;
461
+ let meta = document.head.querySelector(`meta[name='${name}']`);
462
+ if (!meta) {
463
+ meta = document.createElement("meta");
464
+ meta.name = name;
465
+ document.head.appendChild(meta);
466
+ }
467
+ meta.content = content;
468
+ }));
469
+ after(document, operation);
470
+ },
471
+ clearStorage: operation => {
472
+ before(document, operation);
473
+ operate(operation, (() => {
474
+ const {type: type} = operation;
475
+ const storage = type === "session" ? sessionStorage : localStorage;
476
+ storage.clear();
477
+ }));
478
+ after(document, operation);
479
+ },
480
+ go: operation => {
481
+ before(window, operation);
482
+ operate(operation, (() => {
483
+ const {delta: delta} = operation;
484
+ history.go(delta);
485
+ }));
486
+ after(window, operation);
487
+ },
488
+ pushState: operation => {
489
+ before(window, operation);
490
+ operate(operation, (() => {
491
+ const {state: state, title: title, url: url} = operation;
492
+ history.pushState(state || {}, title || "", url);
493
+ }));
494
+ after(window, operation);
495
+ },
496
+ redirectTo: operation => {
497
+ before(window, operation);
498
+ operate(operation, (() => {
499
+ let {url: url, action: action} = operation;
500
+ action = action || "advance";
501
+ if (window.Turbo) window.Turbo.visit(url, {
502
+ action: action
503
+ });
504
+ if (window.Turbolinks) window.Turbolinks.visit(url, {
505
+ action: action
506
+ });
507
+ if (!window.Turbo && !window.Turbolinks) window.location.href = url;
508
+ }));
509
+ after(window, operation);
510
+ },
511
+ reload: operation => {
512
+ before(window, operation);
513
+ operate(operation, (() => {
514
+ window.location.reload();
515
+ }));
516
+ after(window, operation);
517
+ },
518
+ removeStorageItem: operation => {
519
+ before(document, operation);
520
+ operate(operation, (() => {
521
+ const {key: key, type: type} = operation;
522
+ const storage = type === "session" ? sessionStorage : localStorage;
523
+ storage.removeItem(key);
524
+ }));
525
+ after(document, operation);
526
+ },
527
+ replaceState: operation => {
528
+ before(window, operation);
529
+ operate(operation, (() => {
530
+ const {state: state, title: title, url: url} = operation;
531
+ history.replaceState(state || {}, title || "", url);
532
+ }));
533
+ after(window, operation);
534
+ },
535
+ scrollIntoView: operation => {
536
+ const {element: element} = operation;
537
+ before(element, operation);
538
+ operate(operation, (() => {
539
+ element.scrollIntoView(operation);
540
+ }));
541
+ after(element, operation);
542
+ },
543
+ setCookie: operation => {
544
+ before(document, operation);
545
+ operate(operation, (() => {
546
+ const {cookie: cookie} = operation;
547
+ document.cookie = cookie || "";
548
+ }));
549
+ after(document, operation);
550
+ },
551
+ setFocus: operation => {
552
+ const {element: element} = operation;
553
+ before(element, operation);
554
+ operate(operation, (() => {
555
+ assignFocus(element);
556
+ }));
557
+ after(element, operation);
558
+ },
559
+ setStorageItem: operation => {
560
+ before(document, operation);
561
+ operate(operation, (() => {
562
+ const {key: key, value: value, type: type} = operation;
563
+ const storage = type === "session" ? sessionStorage : localStorage;
564
+ storage.setItem(key, value || "");
565
+ }));
566
+ after(document, operation);
567
+ },
568
+ consoleLog: operation => {
569
+ before(document, operation);
570
+ operate(operation, (() => {
571
+ const {message: message, level: level} = operation;
572
+ level && [ "warn", "info", "error" ].includes(level) ? console[level](message || "") : console.log(message || "");
573
+ }));
574
+ after(document, operation);
575
+ },
576
+ consoleTable: operation => {
577
+ before(document, operation);
578
+ operate(operation, (() => {
579
+ const {data: data, columns: columns} = operation;
580
+ console.table(data, columns || []);
581
+ }));
582
+ after(document, operation);
583
+ },
584
+ notification: operation => {
585
+ before(document, operation);
586
+ operate(operation, (() => {
587
+ const {title: title, options: options} = operation;
588
+ Notification.requestPermission().then((result => {
589
+ operation.permission = result;
590
+ if (result === "granted") new Notification(title || "", options);
591
+ }));
592
+ }));
593
+ after(document, operation);
594
+ }
595
+ };
596
+ let operations = Operations;
597
+ const add = newOperations => {
598
+ operations = {
599
+ ...operations,
600
+ ...newOperations
601
+ };
602
+ };
603
+ const addOperations = operations => {
604
+ add(operations);
605
+ };
606
+ const addOperation = (name, operation) => {
607
+ const operations = {};
608
+ operations[name] = operation;
609
+ add(operations);
610
+ };
611
+ var OperationStore = {
612
+ get all() {
613
+ return operations;
614
+ }
615
+ };
616
+ const perform = (operations, options = {
617
+ emitMissingElementWarnings: true
618
+ }) => {
619
+ const batches = {};
620
+ operations.forEach((operation => {
621
+ if (!!operation.batch) batches[operation.batch] = batches[operation.batch] ? ++batches[operation.batch] : 1;
622
+ }));
623
+ operations.forEach((operation => {
624
+ const name = operation.operation;
625
+ try {
626
+ if (operation.selector) {
627
+ operation.element = operation.xpath ? xpathToElement(operation.selector) : document[operation.selectAll ? "querySelectorAll" : "querySelector"](operation.selector);
628
+ } else {
629
+ operation.element = document;
630
+ }
631
+ if (operation.element || options.emitMissingElementWarnings) {
632
+ ActiveElement.set(document.activeElement);
633
+ const cableReadyOperation = OperationStore.all[name];
634
+ if (cableReadyOperation) {
635
+ cableReadyOperation(operation);
636
+ if (!!operation.batch && --batches[operation.batch] === 0) dispatch(document, "cable-ready:batch-complete", {
637
+ batch: operation.batch
638
+ });
639
+ } else {
640
+ console.error(`CableReady couldn't find the "${name}" operation. Make sure you use the camelized form when calling an operation method.`);
641
+ }
642
+ }
643
+ } catch (e) {
644
+ if (operation.element) {
645
+ console.error(`CableReady detected an error in ${name || "operation"}: ${e.message}. If you need to support older browsers make sure you've included the corresponding polyfills. https://docs.stimulusreflex.com/setup#polyfills-for-ie11.`);
646
+ console.error(e);
647
+ } else {
648
+ console.warn(`CableReady ${name || "operation"} failed due to missing DOM element for selector: '${operation.selector}'`);
649
+ }
650
+ }
651
+ }));
652
+ };
653
+ const performAsync = (operations, options = {
654
+ emitMissingElementWarnings: true
655
+ }) => new Promise(((resolve, reject) => {
656
+ try {
657
+ resolve(perform(operations, options));
658
+ } catch (err) {
659
+ reject(err);
660
+ }
661
+ }));
662
+ let consumer;
663
+ const BACKOFF = [ 25, 50, 75, 100, 200, 250, 500, 800, 1e3, 2e3 ];
664
+ const wait = ms => new Promise((resolve => setTimeout(resolve, ms)));
665
+ const getConsumerWithRetry = async (retry = 0) => {
666
+ if (consumer) return consumer;
667
+ if (retry >= BACKOFF.length) {
668
+ throw new Error("Couldn't obtain a Action Cable consumer within 5s");
669
+ }
670
+ await wait(BACKOFF[retry]);
671
+ return await getConsumerWithRetry(retry + 1);
672
+ };
673
+ var CableConsumer = {
674
+ setConsumer(value) {
675
+ consumer = value;
676
+ },
677
+ get consumer() {
678
+ return consumer;
679
+ },
680
+ async getConsumer() {
681
+ return await getConsumerWithRetry();
682
+ }
683
+ };
684
+ class SubscribingElement extends HTMLElement {
685
+ disconnectedCallback() {
686
+ if (this.channel) this.channel.unsubscribe();
687
+ }
688
+ createSubscription(consumer, channel, receivedCallback) {
689
+ this.channel = consumer.subscriptions.create({
690
+ channel: channel,
691
+ identifier: this.identifier
692
+ }, {
693
+ received: receivedCallback
694
+ });
695
+ }
696
+ get preview() {
697
+ return document.documentElement.hasAttribute("data-turbolinks-preview") || document.documentElement.hasAttribute("data-turbo-preview");
698
+ }
699
+ get identifier() {
700
+ return this.getAttribute("identifier");
701
+ }
702
+ }
703
+ class StreamFromElement extends SubscribingElement {
704
+ async connectedCallback() {
705
+ if (this.preview) return;
706
+ const consumer = await CableConsumer.getConsumer();
707
+ if (consumer) {
708
+ this.createSubscription(consumer, "CableReady::Stream", this.performOperations);
709
+ } else {
710
+ console.error("The `stream_from` helper cannot connect without an ActionCable consumer.\nPlease run `rails generate cable_ready:helpers` to fix this.");
711
+ }
712
+ }
713
+ performOperations(data) {
714
+ if (data.cableReady) perform(data.operations);
715
+ }
716
+ }
717
+ const template = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`;
718
+ class UpdatesForElement extends SubscribingElement {
719
+ constructor() {
720
+ super();
721
+ const shadowRoot = this.attachShadow({
722
+ mode: "open"
723
+ });
724
+ shadowRoot.innerHTML = template;
725
+ }
726
+ async connectedCallback() {
727
+ if (this.preview) return;
728
+ this.update = debounce(this.update.bind(this), this.debounce);
729
+ const consumer = await CableConsumer.getConsumer();
730
+ if (consumer) {
731
+ this.createSubscription(consumer, "CableReady::Stream", this.update);
732
+ } else {
733
+ console.error("The `updates-for` helper cannot connect without an ActionCable consumer.\nPlease run `rails generate cable_ready:helpers` to fix this.");
734
+ }
735
+ }
736
+ async update(data) {
737
+ const blocks = Array.from(document.querySelectorAll(this.query), (element => new Block(element)));
738
+ if (blocks[0].element !== this) return;
739
+ ActiveElement.set(document.activeElement);
740
+ this.html = {};
741
+ const uniqueUrls = [ ...new Set(blocks.map((block => block.url))) ];
742
+ await Promise.all(uniqueUrls.map((async url => {
743
+ if (!this.html.hasOwnProperty(url)) {
744
+ const response = await graciouslyFetch(url, {
745
+ "X-Cable-Ready": "update"
746
+ });
747
+ this.html[url] = await response.text();
748
+ }
749
+ })));
750
+ this.index = {};
751
+ blocks.forEach((block => {
752
+ this.index.hasOwnProperty(block.url) ? this.index[block.url]++ : this.index[block.url] = 0;
753
+ block.process(data, this.html, this.index);
754
+ }));
755
+ }
756
+ get query() {
757
+ return `updates-for[identifier="${this.identifier}"]`;
758
+ }
759
+ get identifier() {
760
+ return this.getAttribute("identifier");
761
+ }
762
+ get debounce() {
763
+ return this.hasAttribute("debounce") ? parseInt(this.getAttribute("debounce")) : 20;
764
+ }
765
+ }
766
+ class Block {
767
+ constructor(element) {
768
+ this.element = element;
769
+ }
770
+ async process(data, html, index) {
771
+ if (!this.shouldUpdate(data)) return;
772
+ const blockIndex = index[this.url];
773
+ const template = document.createElement("template");
774
+ this.element.setAttribute("updating", "updating");
775
+ template.innerHTML = String(html[this.url]).trim();
776
+ await this.resolveTurboFrames(template.content);
777
+ const fragments = template.content.querySelectorAll(this.query);
778
+ if (fragments.length <= blockIndex) {
779
+ console.warn(`Update aborted due to insufficient number of elements. The offending url is ${this.url}.`);
780
+ return;
781
+ }
782
+ const operation = {
783
+ element: this.element,
784
+ html: fragments[blockIndex],
785
+ permanentAttributeName: "data-ignore-updates"
786
+ };
787
+ dispatch(this.element, "cable-ready:before-update", operation);
788
+ morphdom__default["default"](this.element, fragments[blockIndex], {
789
+ childrenOnly: true,
790
+ onBeforeElUpdated: shouldMorph(operation),
791
+ onElUpdated: _ => {
792
+ this.element.removeAttribute("updating");
793
+ dispatch(this.element, "cable-ready:after-update", operation);
794
+ assignFocus(operation.focusSelector);
795
+ }
796
+ });
797
+ }
798
+ async resolveTurboFrames(documentFragment) {
799
+ const reloadingTurboFrames = [ ...documentFragment.querySelectorAll('turbo-frame[src]:not([loading="lazy"])') ];
800
+ return Promise.all(reloadingTurboFrames.map((frame => new Promise((async resolve => {
801
+ const frameResponse = await graciouslyFetch(frame.getAttribute("src"), {
802
+ "Turbo-Frame": frame.id,
803
+ "X-Cable-Ready": "update"
804
+ });
805
+ const frameTemplate = document.createElement("template");
806
+ frameTemplate.innerHTML = await frameResponse.text();
807
+ await this.resolveTurboFrames(frameTemplate.content);
808
+ documentFragment.querySelector(`turbo-frame#${frame.id}`).innerHTML = String(frameTemplate.content.querySelector(`turbo-frame#${frame.id}`).innerHTML).trim();
809
+ resolve();
810
+ })))));
811
+ }
812
+ shouldUpdate(data) {
813
+ return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data);
814
+ }
815
+ hasChangesSelectedForUpdate(data) {
816
+ const only = this.element.getAttribute("only");
817
+ return !(only && data.changed && !only.split(" ").some((attribute => data.changed.includes(attribute))));
818
+ }
819
+ get ignoresInnerUpdates() {
820
+ return this.element.hasAttribute("ignore-inner-updates") && this.element.hasAttribute("performing-inner-update");
821
+ }
822
+ get url() {
823
+ return this.element.hasAttribute("url") ? this.element.getAttribute("url") : location.href;
824
+ }
825
+ get identifier() {
826
+ return this.element.identifier;
827
+ }
828
+ get query() {
829
+ return this.element.query;
830
+ }
831
+ }
832
+ const registerInnerUpdates = () => {
833
+ document.addEventListener("stimulus-reflex:before", (event => {
834
+ recursiveMarkUpdatesForElements(event.detail.element);
835
+ }));
836
+ document.addEventListener("stimulus-reflex:after", (event => {
837
+ setTimeout((() => {
838
+ recursiveUnmarkUpdatesForElements(event.detail.element);
839
+ }));
840
+ }));
841
+ document.addEventListener("turbo:submit-start", (event => {
842
+ recursiveMarkUpdatesForElements(event.target);
843
+ }));
844
+ document.addEventListener("turbo:submit-end", (event => {
845
+ setTimeout((() => {
846
+ recursiveUnmarkUpdatesForElements(event.target);
847
+ }));
848
+ }));
849
+ };
850
+ const recursiveMarkUpdatesForElements = leaf => {
851
+ const closestUpdatesFor = leaf && leaf.parentElement.closest("updates-for");
852
+ if (closestUpdatesFor) {
853
+ closestUpdatesFor.setAttribute("performing-inner-update", "");
854
+ recursiveMarkUpdatesForElements(closestUpdatesFor);
855
+ }
856
+ };
857
+ const recursiveUnmarkUpdatesForElements = leaf => {
858
+ const closestUpdatesFor = leaf && leaf.parentElement.closest("updates-for");
859
+ if (closestUpdatesFor) {
860
+ closestUpdatesFor.removeAttribute("performing-inner-update");
861
+ recursiveUnmarkUpdatesForElements(closestUpdatesFor);
862
+ }
863
+ };
864
+ const initialize = (initializeOptions = {}) => {
865
+ const {consumer: consumer} = initializeOptions;
866
+ registerInnerUpdates();
867
+ if (consumer) {
868
+ CableConsumer.setConsumer(consumer);
869
+ } else {
870
+ console.error("CableReady requires a reference to your Action Cable `consumer` for its helpers to function.\nEnsure that you have imported the `CableReady` package as well as `consumer` from your `channels` folder, then call `CableReady.initialize({ consumer })`.");
871
+ }
872
+ if (!customElements.get("stream-from")) {
873
+ customElements.define("stream-from", StreamFromElement);
874
+ }
875
+ if (!customElements.get("updates-for")) {
876
+ customElements.define("updates-for", UpdatesForElement);
877
+ }
878
+ };
879
+ const global = {
880
+ perform: perform,
881
+ performAsync: performAsync,
882
+ shouldMorphCallbacks: shouldMorphCallbacks,
883
+ didMorphCallbacks: didMorphCallbacks,
884
+ initialize: initialize,
885
+ addOperation: addOperation,
886
+ addOperations: addOperations,
887
+ version: packageInfo.version,
888
+ cable: CableConsumer,
889
+ get DOMOperations() {
890
+ console.warn("DEPRECATED: Please use `CableReady.operations` instead of `CableReady.DOMOperations`");
891
+ return OperationStore.all;
892
+ },
893
+ get operations() {
894
+ return OperationStore.all;
895
+ },
896
+ get consumer() {
897
+ return CableConsumer.consumer;
898
+ }
899
+ };
900
+ window.CableReady = global;
901
+ exports.MorphCallbacks = morph_callbacks;
902
+ exports.StreamFromElement = StreamFromElement;
903
+ exports.SubscribingElement = SubscribingElement;
904
+ exports.UpdatesForElement = UpdatesForElement;
905
+ exports.Utils = utils;
906
+ exports["default"] = global;
907
+ Object.defineProperty(exports, "__esModule", {
908
+ value: true
909
+ });
910
+ }));