cable_ready 5.0.0.pre6 → 5.0.0.pre9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }));