cable_ready 5.0.0.pre6 → 5.0.0.pre9

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