stimulus_reflex 3.5.0.pre9 → 3.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of stimulus_reflex might be problematic. Click here for more details.

Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +122 -127
  4. data/README.md +13 -19
  5. data/app/assets/javascripts/stimulus_reflex.js +1017 -523
  6. data/app/assets/javascripts/stimulus_reflex.umd.js +940 -496
  7. data/app/channels/stimulus_reflex/channel.rb +9 -24
  8. data/bin/console +0 -2
  9. data/bin/standardize +2 -1
  10. data/lib/generators/stimulus_reflex/stimulus_reflex_generator.rb +68 -9
  11. data/lib/generators/stimulus_reflex/templates/app/controllers/examples_controller.rb.tt +9 -0
  12. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/consumer.js.tt +6 -0
  13. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.esbuild.tt +4 -0
  14. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.importmap.tt +2 -0
  15. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.shakapacker.tt +5 -0
  16. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.vite.tt +1 -0
  17. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.webpacker.tt +5 -0
  18. data/lib/generators/stimulus_reflex/templates/app/javascript/config/cable_ready.js.tt +4 -0
  19. data/lib/generators/stimulus_reflex/templates/app/javascript/config/index.js.tt +2 -0
  20. data/lib/generators/stimulus_reflex/templates/app/javascript/config/mrujs.js.tt +9 -0
  21. data/lib/generators/stimulus_reflex/templates/app/javascript/config/stimulus_reflex.js.tt +5 -0
  22. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/%file_name%_controller.js.tt +141 -0
  23. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application.js.tt +11 -0
  24. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application_controller.js.tt +74 -0
  25. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.esbuild.tt +7 -0
  26. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.importmap.tt +5 -0
  27. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.shakapacker.tt +5 -0
  28. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.vite.tt +5 -0
  29. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.webpacker.tt +5 -0
  30. data/{test/tmp/app/reflexes/user_reflex.rb → lib/generators/stimulus_reflex/templates/app/reflexes/%file_name%_reflex.rb.tt} +38 -9
  31. data/lib/generators/stimulus_reflex/templates/app/reflexes/application_reflex.rb.tt +27 -0
  32. data/lib/generators/stimulus_reflex/templates/app/views/examples/show.html.erb.tt +207 -0
  33. data/lib/generators/stimulus_reflex/templates/config/initializers/cable_ready.rb +27 -0
  34. data/lib/generators/stimulus_reflex/templates/config/initializers/stimulus_reflex.rb +18 -13
  35. data/lib/generators/stimulus_reflex/templates/esbuild.config.mjs.tt +94 -0
  36. data/lib/install/action_cable.rb +155 -0
  37. data/lib/install/broadcaster.rb +90 -0
  38. data/lib/install/bundle.rb +56 -0
  39. data/lib/install/compression.rb +41 -0
  40. data/lib/install/config.rb +87 -0
  41. data/lib/install/development.rb +110 -0
  42. data/lib/install/esbuild.rb +114 -0
  43. data/lib/install/example.rb +22 -0
  44. data/lib/install/importmap.rb +133 -0
  45. data/lib/install/initializers.rb +25 -0
  46. data/lib/install/mrujs.rb +133 -0
  47. data/lib/install/npm_packages.rb +25 -0
  48. data/lib/install/reflexes.rb +25 -0
  49. data/lib/install/shakapacker.rb +64 -0
  50. data/lib/install/spring.rb +54 -0
  51. data/lib/install/updatable.rb +34 -0
  52. data/lib/install/vite.rb +64 -0
  53. data/lib/install/webpacker.rb +90 -0
  54. data/lib/install/yarn.rb +55 -0
  55. data/lib/stimulus_reflex/broadcasters/broadcaster.rb +15 -8
  56. data/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +7 -8
  57. data/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +10 -10
  58. data/lib/stimulus_reflex/broadcasters/update.rb +3 -0
  59. data/lib/stimulus_reflex/cable_readiness.rb +29 -0
  60. data/lib/stimulus_reflex/cable_ready_channels.rb +6 -5
  61. data/lib/stimulus_reflex/callbacks.rb +17 -1
  62. data/lib/stimulus_reflex/concern_enhancer.rb +6 -4
  63. data/lib/stimulus_reflex/configuration.rb +12 -2
  64. data/lib/stimulus_reflex/dataset.rb +11 -1
  65. data/lib/stimulus_reflex/engine.rb +16 -9
  66. data/lib/stimulus_reflex/html/document.rb +59 -0
  67. data/lib/stimulus_reflex/html/document_fragment.rb +13 -0
  68. data/lib/stimulus_reflex/importmap.rb +6 -3
  69. data/lib/stimulus_reflex/installer.rb +274 -0
  70. data/lib/stimulus_reflex/open_struct_fix.rb +2 -0
  71. data/lib/stimulus_reflex/reflex.rb +40 -31
  72. data/lib/stimulus_reflex/reflex_data.rb +19 -3
  73. data/lib/stimulus_reflex/reflex_factory.rb +6 -3
  74. data/lib/stimulus_reflex/request_parameters.rb +2 -0
  75. data/lib/stimulus_reflex/utils/logger.rb +10 -0
  76. data/lib/stimulus_reflex/utils/sanity_checker.rb +8 -48
  77. data/lib/stimulus_reflex/version.rb +1 -1
  78. data/lib/stimulus_reflex/version_checker.rb +54 -0
  79. data/lib/stimulus_reflex.rb +2 -0
  80. data/lib/tasks/stimulus_reflex/stimulus_reflex.rake +250 -0
  81. data/package.json +36 -28
  82. data/{rollup.config.js → rollup.config.mjs} +6 -24
  83. data/stimulus_reflex.gemspec +16 -19
  84. data/yarn.lock +1331 -748
  85. metadata +129 -79
  86. data/LATEST +0 -1
  87. data/app/assets/javascripts/stimulus_reflex.min.js +0 -2
  88. data/app/assets/javascripts/stimulus_reflex.min.js.map +0 -1
  89. data/app/assets/javascripts/stimulus_reflex.umd.min.js +0 -905
  90. data/app/assets/javascripts/stimulus_reflex.umd.min.js.map +0 -1
  91. data/lib/generators/stimulus_reflex/initializer_generator.rb +0 -14
  92. data/test/broadcasters/broadcaster_test.rb +0 -11
  93. data/test/broadcasters/broadcaster_test_case.rb +0 -39
  94. data/test/broadcasters/nothing_broadcaster_test.rb +0 -31
  95. data/test/broadcasters/page_broadcaster_test.rb +0 -79
  96. data/test/broadcasters/selector_broadcaster_test.rb +0 -173
  97. data/test/callbacks_test.rb +0 -652
  98. data/test/concern_enhancer_test.rb +0 -54
  99. data/test/element_test.rb +0 -254
  100. data/test/generators/stimulus_reflex_generator_test.rb +0 -58
  101. data/test/reflex_test.rb +0 -43
  102. data/test/test_helper.rb +0 -71
  103. data/test/tmp/app/reflexes/application_reflex.rb +0 -12
  104. data/yarn-error.log +0 -4964
@@ -1,39 +1,301 @@
1
1
  (function(global, factory) {
2
2
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@hotwired/stimulus"), require("cable_ready"), require("@rails/actioncable")) : typeof define === "function" && define.amd ? define([ "exports", "@hotwired/stimulus", "cable_ready", "@rails/actioncable" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
3
- factory(global.StimulusReflex = {}, global.Stimulus, global.CableReady, global.actioncable));
3
+ factory(global.StimulusReflex = {}, global.Stimulus, global.CableReady, global.ActionCable));
4
4
  })(this, (function(exports, stimulus, CableReady, actioncable) {
5
5
  "use strict";
6
- function _interopDefaultLegacy(e) {
7
- return e && typeof e === "object" && "default" in e ? e : {
8
- default: e
6
+ /*!
7
+ * Toastify js 1.12.0
8
+ * https://github.com/apvarun/toastify-js
9
+ * @license MIT licensed
10
+ *
11
+ * Copyright (C) 2018 Varun A P
12
+ */ class Toastify {
13
+ defaults={
14
+ oldestFirst: true,
15
+ text: "Toastify is awesome!",
16
+ node: undefined,
17
+ duration: 3e3,
18
+ selector: undefined,
19
+ callback: function() {},
20
+ destination: undefined,
21
+ newWindow: false,
22
+ close: false,
23
+ gravity: "toastify-top",
24
+ positionLeft: false,
25
+ position: "",
26
+ backgroundColor: "",
27
+ avatar: "",
28
+ className: "",
29
+ stopOnFocus: true,
30
+ onClick: function() {},
31
+ offset: {
32
+ x: 0,
33
+ y: 0
34
+ },
35
+ escapeMarkup: true,
36
+ ariaLive: "polite",
37
+ style: {
38
+ background: ""
39
+ }
9
40
  };
10
- }
11
- var CableReady__default = _interopDefaultLegacy(CableReady);
12
- const defaultSchema = {
13
- reflexAttribute: "data-reflex",
14
- reflexPermanentAttribute: "data-reflex-permanent",
15
- reflexRootAttribute: "data-reflex-root",
16
- reflexSuppressLoggingAttribute: "data-reflex-suppress-logging",
17
- reflexDatasetAttribute: "data-reflex-dataset",
18
- reflexDatasetAllAttribute: "data-reflex-dataset-all",
19
- reflexSerializeFormAttribute: "data-reflex-serialize-form",
20
- reflexFormSelectorAttribute: "data-reflex-form-selector",
21
- reflexIncludeInnerHtmlAttribute: "data-reflex-include-inner-html",
22
- reflexIncludeTextContentAttribute: "data-reflex-include-text-content"
23
- };
24
- let schema = {};
25
- var Schema = {
26
- set(application) {
27
- schema = {
28
- ...defaultSchema,
29
- ...application.schema
41
+ constructor(options) {
42
+ this.version = "1.12.0";
43
+ this.options = {};
44
+ this.toastElement = null;
45
+ this._rootElement = document.body;
46
+ this._init(options);
47
+ }
48
+ showToast() {
49
+ this.toastElement = this._buildToast();
50
+ if (typeof this.options.selector === "string") {
51
+ this._rootElement = document.getElementById(this.options.selector);
52
+ } else if (this.options.selector instanceof HTMLElement || this.options.selector instanceof ShadowRoot) {
53
+ this._rootElement = this.options.selector;
54
+ } else {
55
+ this._rootElement = document.body;
56
+ }
57
+ if (!this._rootElement) {
58
+ throw "Root element is not defined";
59
+ }
60
+ this._rootElement.insertBefore(this.toastElement, this._rootElement.firstChild);
61
+ this._reposition();
62
+ if (this.options.duration > 0) {
63
+ this.toastElement.timeOutValue = window.setTimeout((() => {
64
+ this._removeElement(this.toastElement);
65
+ }), this.options.duration);
66
+ }
67
+ return this;
68
+ }
69
+ hideToast() {
70
+ if (this.toastElement.timeOutValue) {
71
+ clearTimeout(this.toastElement.timeOutValue);
72
+ }
73
+ this._removeElement(this.toastElement);
74
+ }
75
+ _init(options) {
76
+ this.options = Object.assign(this.defaults, options);
77
+ if (this.options.backgroundColor) {
78
+ console.warn('DEPRECATION NOTICE: "backgroundColor" is being deprecated. Please use the "style.background" property.');
79
+ }
80
+ this.toastElement = null;
81
+ this.options.gravity = options.gravity === "bottom" ? "toastify-bottom" : "toastify-top";
82
+ this.options.stopOnFocus = options.stopOnFocus === undefined ? true : options.stopOnFocus;
83
+ if (options.backgroundColor) {
84
+ this.options.style.background = options.backgroundColor;
85
+ }
86
+ }
87
+ _buildToast() {
88
+ if (!this.options) {
89
+ throw "Toastify is not initialized";
90
+ }
91
+ let divElement = document.createElement("div");
92
+ divElement.className = `toastify on ${this.options.className}`;
93
+ divElement.className += ` toastify-${this.options.position}`;
94
+ divElement.className += ` ${this.options.gravity}`;
95
+ for (const property in this.options.style) {
96
+ divElement.style[property] = this.options.style[property];
97
+ }
98
+ if (this.options.ariaLive) {
99
+ divElement.setAttribute("aria-live", this.options.ariaLive);
100
+ }
101
+ if (this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) {
102
+ divElement.appendChild(this.options.node);
103
+ } else {
104
+ if (this.options.escapeMarkup) {
105
+ divElement.innerText = this.options.text;
106
+ } else {
107
+ divElement.innerHTML = this.options.text;
108
+ }
109
+ if (this.options.avatar !== "") {
110
+ let avatarElement = document.createElement("img");
111
+ avatarElement.src = this.options.avatar;
112
+ avatarElement.className = "toastify-avatar";
113
+ if (this.options.position == "left") {
114
+ divElement.appendChild(avatarElement);
115
+ } else {
116
+ divElement.insertAdjacentElement("afterbegin", avatarElement);
117
+ }
118
+ }
119
+ }
120
+ if (this.options.close === true) {
121
+ let closeElement = document.createElement("button");
122
+ closeElement.type = "button";
123
+ closeElement.setAttribute("aria-label", "Close");
124
+ closeElement.className = "toast-close";
125
+ closeElement.innerHTML = "✖";
126
+ closeElement.addEventListener("click", (event => {
127
+ event.stopPropagation();
128
+ this._removeElement(this.toastElement);
129
+ window.clearTimeout(this.toastElement.timeOutValue);
130
+ }));
131
+ const width = window.innerWidth > 0 ? window.innerWidth : screen.width;
132
+ if (this.options.position == "left" && width > 360) {
133
+ divElement.insertAdjacentElement("afterbegin", closeElement);
134
+ } else {
135
+ divElement.appendChild(closeElement);
136
+ }
137
+ }
138
+ if (this.options.stopOnFocus && this.options.duration > 0) {
139
+ divElement.addEventListener("mouseover", (event => {
140
+ window.clearTimeout(divElement.timeOutValue);
141
+ }));
142
+ divElement.addEventListener("mouseleave", (() => {
143
+ divElement.timeOutValue = window.setTimeout((() => {
144
+ this._removeElement(divElement);
145
+ }), this.options.duration);
146
+ }));
147
+ }
148
+ if (typeof this.options.destination !== "undefined") {
149
+ divElement.addEventListener("click", (event => {
150
+ event.stopPropagation();
151
+ if (this.options.newWindow === true) {
152
+ window.open(this.options.destination, "_blank");
153
+ } else {
154
+ window.location = this.options.destination;
155
+ }
156
+ }));
157
+ }
158
+ if (typeof this.options.onClick === "function" && typeof this.options.destination === "undefined") {
159
+ divElement.addEventListener("click", (event => {
160
+ event.stopPropagation();
161
+ this.options.onClick();
162
+ }));
163
+ }
164
+ if (typeof this.options.offset === "object") {
165
+ const x = this._getAxisOffsetAValue("x", this.options);
166
+ const y = this._getAxisOffsetAValue("y", this.options);
167
+ const xOffset = this.options.position == "left" ? x : `-${x}`;
168
+ const yOffset = this.options.gravity == "toastify-top" ? y : `-${y}`;
169
+ divElement.style.transform = `translate(${xOffset},${yOffset})`;
170
+ }
171
+ return divElement;
172
+ }
173
+ _removeElement(toastElement) {
174
+ toastElement.className = toastElement.className.replace(" on", "");
175
+ window.setTimeout((() => {
176
+ if (this.options.node && this.options.node.parentNode) {
177
+ this.options.node.parentNode.removeChild(this.options.node);
178
+ }
179
+ if (toastElement.parentNode) {
180
+ toastElement.parentNode.removeChild(toastElement);
181
+ }
182
+ this.options.callback.call(toastElement);
183
+ this._reposition();
184
+ }), 400);
185
+ }
186
+ _reposition() {
187
+ let topLeftOffsetSize = {
188
+ top: 15,
189
+ bottom: 15
30
190
  };
31
- for (const attribute in schema) {
32
- Object.defineProperty(this, attribute.slice(0, -9), {
33
- get: () => schema[attribute]
34
- });
191
+ let topRightOffsetSize = {
192
+ top: 15,
193
+ bottom: 15
194
+ };
195
+ let offsetSize = {
196
+ top: 15,
197
+ bottom: 15
198
+ };
199
+ let allToasts = this._rootElement.querySelectorAll(".toastify");
200
+ let classUsed;
201
+ for (let i = 0; i < allToasts.length; i++) {
202
+ if (allToasts[i].classList.contains("toastify-top") === true) {
203
+ classUsed = "toastify-top";
204
+ } else {
205
+ classUsed = "toastify-bottom";
206
+ }
207
+ let height = allToasts[i].offsetHeight;
208
+ classUsed = classUsed.substr(9, classUsed.length - 1);
209
+ let offset = 15;
210
+ let width = window.innerWidth > 0 ? window.innerWidth : screen.width;
211
+ if (width <= 360) {
212
+ allToasts[i].style[classUsed] = `${offsetSize[classUsed]}px`;
213
+ offsetSize[classUsed] += height + offset;
214
+ } else {
215
+ if (allToasts[i].classList.contains("toastify-left") === true) {
216
+ allToasts[i].style[classUsed] = `${topLeftOffsetSize[classUsed]}px`;
217
+ topLeftOffsetSize[classUsed] += height + offset;
218
+ } else {
219
+ allToasts[i].style[classUsed] = `${topRightOffsetSize[classUsed]}px`;
220
+ topRightOffsetSize[classUsed] += height + offset;
221
+ }
222
+ }
35
223
  }
36
224
  }
225
+ _getAxisOffsetAValue(axis, options) {
226
+ if (options.offset[axis]) {
227
+ if (isNaN(options.offset[axis])) {
228
+ return options.offset[axis];
229
+ } else {
230
+ return `${options.offset[axis]}px`;
231
+ }
232
+ }
233
+ return "0px";
234
+ }
235
+ }
236
+ function StartToastifyInstance(options) {
237
+ return new Toastify(options);
238
+ }
239
+ CableReady.operations.stimulusReflexVersionMismatch = operation => {
240
+ const levels = {
241
+ info: {},
242
+ success: {
243
+ background: "#198754",
244
+ color: "white"
245
+ },
246
+ warn: {
247
+ background: "#ffc107",
248
+ color: "black"
249
+ },
250
+ error: {
251
+ background: "#dc3545",
252
+ color: "white"
253
+ }
254
+ };
255
+ const defaults = {
256
+ selector: setupToastify(),
257
+ close: true,
258
+ duration: 30 * 1e3,
259
+ gravity: "bottom",
260
+ position: "right",
261
+ newWindow: true,
262
+ style: levels[operation.level || "info"]
263
+ };
264
+ StartToastifyInstance({
265
+ ...defaults,
266
+ ...operation
267
+ }).showToast();
268
+ };
269
+ function setupToastify() {
270
+ const id = "stimulus-reflex-toast-element";
271
+ let element = document.querySelector(`#${id}`);
272
+ if (!element) {
273
+ element = document.createElement("div");
274
+ element.id = id;
275
+ document.documentElement.appendChild(element);
276
+ const styles = document.createElement("style");
277
+ styles.innerHTML = `\n #${id} .toastify {\n padding: 12px 20px;\n color: #ffffff;\n display: inline-block;\n background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);\n background: linear-gradient(135deg, #73a5ff, #5477f5);\n position: fixed;\n opacity: 0;\n transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);\n border-radius: 2px;\n cursor: pointer;\n text-decoration: none;\n max-width: calc(50% - 20px);\n z-index: 2147483647;\n bottom: -150px;\n right: 15px;\n }\n\n #${id} .toastify.on {\n opacity: 1;\n }\n\n #${id} .toast-close {\n background: transparent;\n border: 0;\n color: white;\n cursor: pointer;\n font-family: inherit;\n font-size: 1em;\n opacity: 0.4;\n padding: 0 5px;\n }\n `;
278
+ document.head.appendChild(styles);
279
+ }
280
+ return element;
281
+ }
282
+ let deprecationWarnings = true;
283
+ var Deprecate = {
284
+ get enabled() {
285
+ return deprecationWarnings;
286
+ },
287
+ get disabled() {
288
+ return !deprecationWarnings;
289
+ },
290
+ get value() {
291
+ return deprecationWarnings;
292
+ },
293
+ set(value) {
294
+ deprecationWarnings = !!value;
295
+ },
296
+ set deprecate(value) {
297
+ deprecationWarnings = !!value;
298
+ }
37
299
  };
38
300
  let debugging = false;
39
301
  var Debug$1 = {
@@ -53,70 +315,35 @@
53
315
  debugging = !!value;
54
316
  }
55
317
  };
56
- const reflexes = {};
57
- const request = (reflexId, target, args, controller, element, controllerElement) => {
58
- const reflex = reflexes[reflexId];
59
- if (Debug$1.disabled || reflex.promise.data.suppressLogging) return;
60
- reflex.timestamp = new Date;
61
- console.log(`↑ stimulus ↑ ${target}`, {
62
- reflexId: reflexId,
63
- args: args,
64
- controller: controller,
65
- element: element,
66
- controllerElement: controllerElement
67
- });
68
- };
69
- const success = (event, halted) => {
70
- const {detail: detail} = event || {};
71
- const {selector: selector, payload: payload} = detail || {};
72
- const {reflexId: reflexId, target: target, morph: morph} = detail.stimulusReflex || {};
73
- const reflex = reflexes[reflexId];
74
- if (Debug$1.disabled || reflex.promise.data.suppressLogging) return;
75
- const progress = reflex.totalOperations > 1 ? ` ${reflex.completedOperations}/${reflex.totalOperations}` : "";
76
- const duration = reflex.timestamp ? `in ${new Date - reflex.timestamp}ms` : "CLONED";
77
- const operation = event.type.split(":")[1].split("-").slice(1).join("_");
78
- console.log(`↓ reflex ↓ ${target} → ${selector || "∞"}${progress} ${duration}`, {
79
- reflexId: reflexId,
80
- morph: morph,
81
- operation: operation,
82
- halted: halted,
83
- payload: payload
84
- });
85
- };
86
- const error$1 = event => {
87
- const {detail: detail} = event || {};
88
- const {reflexId: reflexId, target: target, payload: payload} = detail.stimulusReflex || {};
89
- const reflex = reflexes[reflexId];
90
- if (Debug$1.disabled || reflex.promise.data.suppressLogging) return;
91
- const duration = reflex.timestamp ? `in ${new Date - reflex.timestamp}ms` : "CLONED";
92
- console.log(`↓ reflex ↓ ${target} ${duration} %cERROR: ${event.detail.body}`, "color: #f00;", {
93
- reflexId: reflexId,
94
- payload: payload
95
- });
96
- };
97
- var Log = {
98
- request: request,
99
- success: success,
100
- error: error$1
318
+ const defaultSchema = {
319
+ reflexAttribute: "data-reflex",
320
+ reflexPermanentAttribute: "data-reflex-permanent",
321
+ reflexRootAttribute: "data-reflex-root",
322
+ reflexSuppressLoggingAttribute: "data-reflex-suppress-logging",
323
+ reflexDatasetAttribute: "data-reflex-dataset",
324
+ reflexDatasetAllAttribute: "data-reflex-dataset-all",
325
+ reflexSerializeFormAttribute: "data-reflex-serialize-form",
326
+ reflexFormSelectorAttribute: "data-reflex-form-selector",
327
+ reflexIncludeInnerHtmlAttribute: "data-reflex-include-inner-html",
328
+ reflexIncludeTextContentAttribute: "data-reflex-include-text-content"
101
329
  };
102
- let deprecationWarnings = true;
103
- var Deprecate = {
104
- get enabled() {
105
- return deprecationWarnings;
106
- },
107
- get disabled() {
108
- return !deprecationWarnings;
109
- },
110
- get value() {
111
- return deprecationWarnings;
112
- },
113
- set(value) {
114
- deprecationWarnings = !!value;
115
- },
116
- set deprecate(value) {
117
- deprecationWarnings = !!value;
330
+ let schema = {};
331
+ var Schema = {
332
+ set(application) {
333
+ schema = {
334
+ ...defaultSchema,
335
+ ...application.schema
336
+ };
337
+ for (const attribute in schema) {
338
+ const attributeName = attribute.slice(0, -9);
339
+ Object.defineProperty(this, attributeName, {
340
+ get: () => schema[attribute],
341
+ configurable: true
342
+ });
343
+ }
118
344
  }
119
345
  };
346
+ const {debounce: debounce, dispatch: dispatch, xpathToElement: xpathToElement, xpathToElementArray: xpathToElementArray} = CableReady.Utils;
120
347
  const uuidv4 = () => {
121
348
  const crypto = window.crypto || window.msCrypto;
122
349
  return ([ 1e7 ] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)));
@@ -141,33 +368,19 @@
141
368
  if (uppercaseFirstLetter) value = value.substr(0, 1).toUpperCase() + value.substr(1);
142
369
  return value;
143
370
  };
144
- const debounce = (callback, delay = 250) => {
145
- let timeoutId;
146
- return (...args) => {
147
- clearTimeout(timeoutId);
148
- timeoutId = setTimeout((() => {
149
- timeoutId = null;
150
- callback(...args);
151
- }), delay);
152
- };
153
- };
371
+ const XPathToElement = xpathToElement;
372
+ const XPathToArray = xpathToElementArray;
373
+ const emitEvent = (name, detail = {}) => dispatch(document, name, detail);
154
374
  const extractReflexName = reflexString => {
155
375
  const match = reflexString.match(/(?:.*->)?(.*?)(?:Reflex)?#/);
156
376
  return match ? match[1] : "";
157
377
  };
158
- const emitEvent = (event, detail) => {
159
- document.dispatchEvent(new CustomEvent(event, {
160
- bubbles: true,
161
- cancelable: false,
162
- detail: detail
163
- }));
164
- if (window.jQuery) window.jQuery(document).trigger(event, detail);
165
- };
166
378
  const elementToXPath = element => {
167
379
  if (element.id !== "") return "//*[@id='" + element.id + "']";
168
380
  if (element === document.body) return "/html/body";
381
+ if (element.nodeName === "HTML") return "/html";
169
382
  let ix = 0;
170
- const siblings = element?.parentNode ? element.parentNode.childNodes : [];
383
+ const siblings = element && element.parentNode ? element.parentNode.childNodes : [];
171
384
  for (var i = 0; i < siblings.length; i++) {
172
385
  const sibling = siblings[i];
173
386
  if (sibling === element) {
@@ -181,14 +394,355 @@
181
394
  }
182
395
  }
183
396
  };
184
- const XPathToElement = xpath => document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
185
- const XPathToArray = (xpath, reverse = false) => {
186
- const snapshotList = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
187
- const snapshots = [];
188
- for (let i = 0; i < snapshotList.snapshotLength; i++) {
189
- snapshots.push(snapshotList.snapshotItem(i));
397
+ const elementInvalid = element => element.type === "number" && element.validity && element.validity.badInput;
398
+ const getReflexElement = (args, element) => args[0] && args[0].nodeType === Node.ELEMENT_NODE ? args.shift() : element;
399
+ const getReflexOptions = args => {
400
+ const options = {};
401
+ if (args[0] && typeof args[0] === "object" && Object.keys(args[0]).filter((key => [ "id", "attrs", "selectors", "reflexId", "resolveLate", "serializeForm", "suppressLogging", "includeInnerHTML", "includeTextContent" ].includes(key))).length) {
402
+ const opts = args.shift();
403
+ Object.keys(opts).forEach((o => {
404
+ if (o === "reflexId") {
405
+ if (Deprecate.enabled) console.warn("reflexId option will be removed in v4. Use id instead.");
406
+ options["id"] = opts["reflexId"];
407
+ } else options[o] = opts[o];
408
+ }));
409
+ }
410
+ return options;
411
+ };
412
+ const getReflexRoots = element => {
413
+ let list = [];
414
+ while (list.length === 0 && element) {
415
+ let reflexRoot = element.getAttribute(Schema.reflexRoot);
416
+ if (reflexRoot) {
417
+ if (reflexRoot.length === 0 && element.id) reflexRoot = `#${element.id}`;
418
+ const selectors = reflexRoot.split(",").filter((s => s.trim().length));
419
+ if (Debug$1.enabled && selectors.length === 0) {
420
+ console.error(`No value found for ${Schema.reflexRoot}. Add an #id to the element or provide a value for ${Schema.reflexRoot}.`, element);
421
+ }
422
+ list = list.concat(selectors.filter((s => document.querySelector(s))));
423
+ }
424
+ element = element.parentElement ? element.parentElement.closest(`[${Schema.reflexRoot}]`) : null;
425
+ }
426
+ return list;
427
+ };
428
+ const reflexNameToControllerIdentifier = reflexName => reflexName.replace(/([a-z0–9])([A-Z])/g, "$1-$2").replace(/(::)/g, "--").replace(/-reflex$/gi, "").toLowerCase();
429
+ const stages = [ "created", "before", "delivered", "queued", "after", "finalized", "success", "error", "halted", "forbidden" ];
430
+ let lastReflex;
431
+ const reflexes = new Proxy({}, {
432
+ get: function(target, prop) {
433
+ if (stages.includes(prop)) return Object.fromEntries(Object.entries(target).filter((([_, reflex]) => reflex.stage === prop))); else if (prop === "last") return lastReflex; else if (prop === "all") return target;
434
+ return Reflect.get(...arguments);
435
+ },
436
+ set: function(target, prop, value) {
437
+ target[prop] = value;
438
+ lastReflex = value;
439
+ return true;
440
+ }
441
+ });
442
+ const invokeLifecycleMethod = (reflex, stage) => {
443
+ const specificLifecycleMethod = reflex.controller[[ "before", "after", "finalize" ].includes(stage) ? `${stage}${camelize(reflex.action)}` : `${camelize(reflex.action, false)}${camelize(stage)}`];
444
+ const genericLifecycleMethod = reflex.controller[[ "before", "after", "finalize" ].includes(stage) ? `${stage}Reflex` : `reflex${camelize(stage)}`];
445
+ if (typeof specificLifecycleMethod === "function") {
446
+ specificLifecycleMethod.call(reflex.controller, reflex.element, reflex.target, reflex.error, reflex.id, reflex.payload);
447
+ }
448
+ if (typeof genericLifecycleMethod === "function") {
449
+ genericLifecycleMethod.call(reflex.controller, reflex.element, reflex.target, reflex.error, reflex.id, reflex.payload);
450
+ }
451
+ };
452
+ const dispatchLifecycleEvent = (reflex, stage) => {
453
+ if (!reflex.controller.element.parentElement) {
454
+ if (Debug$1.enabled && !reflex.warned) {
455
+ console.warn(`StimulusReflex was not able execute callbacks or emit events for "${stage}" or later life-cycle stages for this Reflex. The StimulusReflex Controller Element is no longer present in the DOM. Could you move the StimulusReflex Controller to an element higher in your DOM?`);
456
+ reflex.warned = true;
457
+ }
458
+ return;
459
+ }
460
+ reflex.stage = stage;
461
+ reflex.lifecycle.push(stage);
462
+ const event = `stimulus-reflex:${stage}`;
463
+ const action = `${event}:${reflex.action}`;
464
+ const detail = {
465
+ reflex: reflex.target,
466
+ controller: reflex.controller,
467
+ id: reflex.id,
468
+ element: reflex.element,
469
+ payload: reflex.payload
470
+ };
471
+ const options = {
472
+ bubbles: true,
473
+ cancelable: false,
474
+ detail: detail
475
+ };
476
+ reflex.controller.element.dispatchEvent(new CustomEvent(event, options));
477
+ reflex.controller.element.dispatchEvent(new CustomEvent(action, options));
478
+ if (window.jQuery) {
479
+ window.jQuery(reflex.controller.element).trigger(event, detail);
480
+ window.jQuery(reflex.controller.element).trigger(action, detail);
481
+ }
482
+ };
483
+ document.addEventListener("stimulus-reflex:before", (event => invokeLifecycleMethod(reflexes[event.detail.id], "before")), true);
484
+ document.addEventListener("stimulus-reflex:queued", (event => invokeLifecycleMethod(reflexes[event.detail.id], "queued")), true);
485
+ document.addEventListener("stimulus-reflex:delivered", (event => invokeLifecycleMethod(reflexes[event.detail.id], "delivered")), true);
486
+ document.addEventListener("stimulus-reflex:success", (event => {
487
+ const reflex = reflexes[event.detail.id];
488
+ invokeLifecycleMethod(reflex, "success");
489
+ dispatchLifecycleEvent(reflex, "after");
490
+ }), true);
491
+ document.addEventListener("stimulus-reflex:nothing", (event => dispatchLifecycleEvent(reflexes[event.detail.id], "success")), true);
492
+ document.addEventListener("stimulus-reflex:error", (event => {
493
+ const reflex = reflexes[event.detail.id];
494
+ invokeLifecycleMethod(reflex, "error");
495
+ dispatchLifecycleEvent(reflex, "after");
496
+ }), true);
497
+ document.addEventListener("stimulus-reflex:halted", (event => invokeLifecycleMethod(reflexes[event.detail.id], "halted")), true);
498
+ document.addEventListener("stimulus-reflex:forbidden", (event => invokeLifecycleMethod(reflexes[event.detail.id], "forbidden")), true);
499
+ document.addEventListener("stimulus-reflex:after", (event => invokeLifecycleMethod(reflexes[event.detail.id], "after")), true);
500
+ document.addEventListener("stimulus-reflex:finalize", (event => invokeLifecycleMethod(reflexes[event.detail.id], "finalize")), true);
501
+ let app = {};
502
+ var App = {
503
+ get app() {
504
+ return app;
505
+ },
506
+ set(application) {
507
+ app = application;
508
+ }
509
+ };
510
+ let isolationMode = false;
511
+ var IsolationMode = {
512
+ get disabled() {
513
+ return !isolationMode;
514
+ },
515
+ set(value) {
516
+ isolationMode = value;
517
+ if (Deprecate.enabled && !isolationMode) {
518
+ document.addEventListener("DOMContentLoaded", (() => console.warn("Deprecation warning: the next version of StimulusReflex will standardize isolation mode, and the isolate option will be removed.\nPlease update your applications to assume that every tab will be isolated. Use CableReady operations to broadcast updates to other tabs and users.")), {
519
+ once: true
520
+ });
521
+ }
522
+ }
523
+ };
524
+ class Reflex {
525
+ constructor(data, controller) {
526
+ this.data = data.valueOf();
527
+ this.controller = controller;
528
+ this.element = data.reflexElement;
529
+ this.id = data.id;
530
+ this.error = null;
531
+ this.payload = null;
532
+ this.stage = "created";
533
+ this.lifecycle = [ "created" ];
534
+ this.warned = false;
535
+ this.target = data.target;
536
+ this.action = data.target.split("#")[1];
537
+ this.selector = null;
538
+ this.morph = null;
539
+ this.operation = null;
540
+ this.timestamp = new Date;
541
+ this.cloned = false;
542
+ }
543
+ get getPromise() {
544
+ const promise = new Promise(((resolve, reject) => {
545
+ this.promise = {
546
+ resolve: resolve,
547
+ reject: reject,
548
+ data: this.data
549
+ };
550
+ }));
551
+ promise.id = this.id;
552
+ Object.defineProperty(promise, "reflexId", {
553
+ get() {
554
+ if (Deprecate.enabled) console.warn("reflexId is deprecated and will be removed from v4. Use id instead.");
555
+ return this.id;
556
+ }
557
+ });
558
+ promise.reflex = this;
559
+ if (Debug$1.enabled) promise.catch((() => {}));
560
+ return promise;
561
+ }
562
+ }
563
+ const received = data => {
564
+ if (!data.cableReady) return;
565
+ if (data.version.replace(".pre", "-pre") !== CableReady.version) {
566
+ if (Debug$1.enabled) console.error(`Reflex failed due to cable_ready gem/NPM package version mismatch. Package versions must match exactly.\nNote that if you are using pre-release builds, gems use the "x.y.z.preN" version format, while NPM packages use "x.y.z-preN".\n\ncable_ready gem: ${data.version}\ncable_ready NPM: ${CableReady.version}`);
567
+ return;
568
+ }
569
+ let reflexOperations = [];
570
+ for (let i = data.operations.length - 1; i >= 0; i--) {
571
+ if (data.operations[i].stimulusReflex) {
572
+ reflexOperations.push(data.operations[i]);
573
+ data.operations.splice(i, 1);
574
+ }
575
+ }
576
+ if (reflexOperations.some((operation => operation.stimulusReflex.url !== location.href))) {
577
+ if (Debug$1.enabled) {
578
+ console.error("Reflex failed due to mismatched URL.");
579
+ return;
580
+ }
581
+ }
582
+ let reflexData;
583
+ if (reflexOperations.length) {
584
+ reflexData = reflexOperations[0].stimulusReflex;
585
+ reflexData.payload = reflexOperations[0].payload;
586
+ }
587
+ if (reflexData) {
588
+ const {id: id, payload: payload} = reflexData;
589
+ let reflex;
590
+ if (!reflexes[id] && IsolationMode.disabled) {
591
+ const controllerElement = XPathToElement(reflexData.xpathController);
592
+ const reflexElement = XPathToElement(reflexData.xpathElement);
593
+ controllerElement.reflexController = controllerElement.reflexController || {};
594
+ controllerElement.reflexData = controllerElement.reflexData || {};
595
+ controllerElement.reflexError = controllerElement.reflexError || {};
596
+ const controller = App.app.getControllerForElementAndIdentifier(controllerElement, reflexData.reflexController);
597
+ controllerElement.reflexController[id] = controller;
598
+ controllerElement.reflexData[id] = reflexData;
599
+ reflex = new Reflex(reflexData, controller);
600
+ reflexes[id] = reflex;
601
+ reflex.cloned = true;
602
+ reflex.element = reflexElement;
603
+ controller.lastReflex = reflex;
604
+ dispatchLifecycleEvent(reflex, "before");
605
+ reflex.getPromise;
606
+ } else {
607
+ reflex = reflexes[id];
608
+ }
609
+ if (reflex) {
610
+ reflex.payload = payload;
611
+ reflex.totalOperations = reflexOperations.length;
612
+ reflex.pendingOperations = reflexOperations.length;
613
+ reflex.completedOperations = 0;
614
+ reflex.piggybackOperations = data.operations;
615
+ CableReady.perform(reflexOperations);
616
+ }
617
+ } else {
618
+ if (data.operations.length && reflexes[data.operations[0].reflexId]) {
619
+ CableReady.perform(data.operations);
620
+ }
621
+ }
622
+ };
623
+ let consumer;
624
+ let params;
625
+ let subscription;
626
+ let active;
627
+ const initialize$1 = (consumerValue, paramsValue) => {
628
+ consumer = consumerValue;
629
+ params = paramsValue;
630
+ document.addEventListener("DOMContentLoaded", (() => {
631
+ active = false;
632
+ connectionStatusClass();
633
+ if (Deprecate.enabled && consumerValue) console.warn("Deprecation warning: the next version of StimulusReflex will obtain a reference to consumer via the Stimulus application object.\nPlease add 'application.consumer = consumer' to your index.js after your Stimulus application has been established, and remove the consumer key from your StimulusReflex initialize() options object.");
634
+ }));
635
+ document.addEventListener("turbolinks:load", connectionStatusClass);
636
+ document.addEventListener("turbo:load", connectionStatusClass);
637
+ };
638
+ const subscribe = controller => {
639
+ if (subscription) return;
640
+ consumer = consumer || controller.application.consumer || actioncable.createConsumer();
641
+ const {channel: channel} = controller.StimulusReflex;
642
+ const request = {
643
+ channel: channel,
644
+ ...params
645
+ };
646
+ const identifier = JSON.stringify(request);
647
+ subscription = consumer.subscriptions.findAll(identifier)[0] || consumer.subscriptions.create(request, {
648
+ received: received,
649
+ connected: connected,
650
+ rejected: rejected,
651
+ disconnected: disconnected
652
+ });
653
+ };
654
+ const connected = () => {
655
+ active = true;
656
+ connectionStatusClass();
657
+ emitEvent("stimulus-reflex:connected");
658
+ Object.values(reflexes.queued).forEach((reflex => {
659
+ subscription.send(reflex.data);
660
+ dispatchLifecycleEvent(reflex, "delivered");
661
+ }));
662
+ };
663
+ const rejected = () => {
664
+ active = false;
665
+ connectionStatusClass();
666
+ emitEvent("stimulus-reflex:rejected");
667
+ if (Debug.enabled) console.warn("Channel subscription was rejected.");
668
+ };
669
+ const disconnected = willAttemptReconnect => {
670
+ active = false;
671
+ connectionStatusClass();
672
+ emitEvent("stimulus-reflex:disconnected", willAttemptReconnect);
673
+ };
674
+ const deliver = reflex => {
675
+ if (active) {
676
+ subscription.send(reflex.data);
677
+ dispatchLifecycleEvent(reflex, "delivered");
678
+ } else dispatchLifecycleEvent(reflex, "queued");
679
+ };
680
+ const connectionStatusClass = () => {
681
+ const list = document.body.classList;
682
+ if (!(list.contains("stimulus-reflex-connected") || list.contains("stimulus-reflex-disconnected"))) {
683
+ list.add(active ? "stimulus-reflex-connected" : "stimulus-reflex-disconnected");
684
+ return;
685
+ }
686
+ if (active) {
687
+ list.replace("stimulus-reflex-disconnected", "stimulus-reflex-connected");
688
+ } else {
689
+ list.replace("stimulus-reflex-connected", "stimulus-reflex-disconnected");
190
690
  }
191
- return reverse ? snapshots.reverse() : snapshots;
691
+ };
692
+ var ActionCableTransport = {
693
+ subscribe: subscribe,
694
+ deliver: deliver,
695
+ initialize: initialize$1
696
+ };
697
+ const request = reflex => {
698
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
699
+ console.log(`↑ stimulus ↑ ${reflex.target}`, {
700
+ id: reflex.id,
701
+ args: reflex.data.args,
702
+ controller: reflex.controller.identifier,
703
+ element: reflex.element,
704
+ controllerElement: reflex.controller.element
705
+ });
706
+ };
707
+ const success = reflex => {
708
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
709
+ const output = {
710
+ id: reflex.id,
711
+ morph: reflex.morph,
712
+ payload: reflex.payload
713
+ };
714
+ if (reflex.operation !== "dispatch_event") output.operation = reflex.operation;
715
+ console.log(`↓ reflex ↓ ${reflex.target} → ${reflex.selector || "∞"}${progress(reflex)} ${duration(reflex)}`, output);
716
+ };
717
+ const halted$1 = reflex => {
718
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
719
+ console.log(`↓ reflex ↓ ${reflex.target} ${duration(reflex)} %cHALTED`, "color: #ffa500;", {
720
+ id: reflex.id,
721
+ payload: reflex.payload
722
+ });
723
+ };
724
+ const forbidden$1 = reflex => {
725
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
726
+ console.log(`↓ reflex ↓ ${reflex.target} ${duration(reflex)} %cFORBIDDEN`, "color: #BF40BF;", {
727
+ id: reflex.id,
728
+ payload: reflex.payload
729
+ });
730
+ };
731
+ const error$1 = reflex => {
732
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
733
+ console.log(`↓ reflex ↓ ${reflex.target} ${duration(reflex)} %cERROR: ${reflex.error}`, "color: #f00;", {
734
+ id: reflex.id,
735
+ payload: reflex.payload
736
+ });
737
+ };
738
+ const duration = reflex => !reflex.cloned ? `in ${new Date - reflex.timestamp}ms` : "CLONED";
739
+ const progress = reflex => reflex.totalOperations > 1 ? ` ${reflex.completedOperations}/${reflex.totalOperations}` : "";
740
+ var Log = {
741
+ request: request,
742
+ success: success,
743
+ halted: halted$1,
744
+ forbidden: forbidden$1,
745
+ error: error$1
192
746
  };
193
747
  const multipleInstances = element => {
194
748
  if ([ "checkbox", "radio" ].includes(element.type)) {
@@ -198,8 +752,8 @@
198
752
  };
199
753
  const collectCheckedOptions = element => Array.from(element.querySelectorAll("option:checked")).concat(Array.from(document.querySelectorAll(`input[type="${element.type}"][name="${element.name}"]`)).filter((elem => elem.checked))).map((o => o.value));
200
754
  const attributeValue = (values = []) => {
201
- const value = values.filter((v => v && String(v).length)).map((v => v.trim())).join(" ").trim();
202
- return value.length ? value : null;
755
+ const value = Array.from(new Set(values.filter((v => v && String(v).length)).map((v => v.trim())))).join(" ").trim();
756
+ return value.length > 0 ? value : null;
203
757
  };
204
758
  const attributeValues = value => {
205
759
  if (!value) return [];
@@ -307,217 +861,78 @@
307
861
  }
308
862
  return attrs;
309
863
  };
310
- let isolationMode = false;
311
- var IsolationMode = {
312
- get disabled() {
313
- return !isolationMode;
314
- },
315
- set(value) {
316
- isolationMode = value;
317
- }
864
+ var name = "stimulus_reflex";
865
+ var version = "3.5.0-rc1";
866
+ var description = "Build reactive applications with the Rails tooling you already know and love.";
867
+ var keywords = [ "ruby", "rails", "websockets", "actioncable", "turbolinks", "reactive", "cable", "ujs", "ssr", "stimulus", "reflex", "stimulus_reflex", "dom", "morphdom" ];
868
+ var homepage = "https://docs.stimulusreflex.com";
869
+ var bugs = "https://github.com/stimulusreflex/stimulus_reflex/issues";
870
+ var repository = "https://github.com/stimulusreflex/stimulus_reflex";
871
+ var license = "MIT";
872
+ var author = "Nathan Hopkins <natehop@gmail.com>";
873
+ var contributors = [ "Andrew Mason <andrewmcodes@protonmail.com>", "Julian Rubisch <julian@julianrubisch.at>", "Marco Roth <marco.roth@intergga.ch>", "Nathan Hopkins <natehop@gmail.com>" ];
874
+ var main = "./dist/stimulus_reflex.js";
875
+ var module = "./dist/stimulus_reflex.js";
876
+ var browser = "./dist/stimulus_reflex.js";
877
+ var unpkg = "./dist/stimulus_reflex.umd.js";
878
+ var umd = "./dist/stimulus_reflex.umd.js";
879
+ var files = [ "dist/*", "javascript/*" ];
880
+ var scripts = {
881
+ lint: "yarn run format --check",
882
+ format: "yarn run prettier-standard ./javascript/**/*.js rollup.config.mjs",
883
+ build: "yarn rollup -c",
884
+ "build:watch": "yarn rollup -wc",
885
+ watch: "yarn build:watch",
886
+ test: "web-test-runner javascript/test/**/*.test.js",
887
+ "test:watch": "yarn test --watch",
888
+ "docs:dev": "vitepress dev docs",
889
+ "docs:build": "vitepress build docs && cp docs/_redirects docs/.vitepress/dist",
890
+ "docs:preview": "vitepress preview docs"
318
891
  };
319
- const invokeLifecycleMethod = (stage, reflexElement, controllerElement, reflexId, payload) => {
320
- if (!controllerElement || !controllerElement.reflexData[reflexId]) return;
321
- const controller = controllerElement.reflexController[reflexId];
322
- const reflex = controllerElement.reflexData[reflexId].target;
323
- const reflexMethodName = reflex.split("#")[1];
324
- const specificLifecycleMethodName = [ "before", "after", "finalize" ].includes(stage) ? `${stage}${camelize(reflexMethodName)}` : `${camelize(reflexMethodName, false)}${camelize(stage)}`;
325
- const specificLifecycleMethod = controller[specificLifecycleMethodName];
326
- const genericLifecycleMethodName = [ "before", "after", "finalize" ].includes(stage) ? `${stage}Reflex` : `reflex${camelize(stage)}`;
327
- const genericLifecycleMethod = controller[genericLifecycleMethodName];
328
- if (typeof specificLifecycleMethod === "function") {
329
- specificLifecycleMethod.call(controller, reflexElement, reflex, controllerElement.reflexError[reflexId], reflexId, payload);
330
- }
331
- if (typeof genericLifecycleMethod === "function") {
332
- genericLifecycleMethod.call(controller, reflexElement, reflex, controllerElement.reflexError[reflexId], reflexId, payload);
333
- }
334
- if (reflexes[reflexId] && stage === reflexes[reflexId].finalStage) {
335
- Reflect.deleteProperty(controllerElement.reflexController, reflexId);
336
- Reflect.deleteProperty(controllerElement.reflexData, reflexId);
337
- Reflect.deleteProperty(controllerElement.reflexError, reflexId);
338
- }
892
+ var peerDependencies = {
893
+ "@hotwired/stimulus": ">= 3.0"
339
894
  };
340
- document.addEventListener("stimulus-reflex:before", (event => invokeLifecycleMethod("before", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload)), true);
341
- document.addEventListener("stimulus-reflex:success", (event => {
342
- invokeLifecycleMethod("success", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload);
343
- dispatchLifecycleEvent("after", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload);
344
- }), true);
345
- document.addEventListener("stimulus-reflex:nothing", (event => {
346
- dispatchLifecycleEvent("success", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload);
347
- }), true);
348
- document.addEventListener("stimulus-reflex:error", (event => {
349
- invokeLifecycleMethod("error", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload);
350
- dispatchLifecycleEvent("after", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload);
351
- }), true);
352
- document.addEventListener("stimulus-reflex:halted", (event => invokeLifecycleMethod("halted", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload)), true);
353
- document.addEventListener("stimulus-reflex:after", (event => invokeLifecycleMethod("after", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload)), true);
354
- document.addEventListener("stimulus-reflex:finalize", (event => invokeLifecycleMethod("finalize", event.detail.element, event.detail.controller.element, event.detail.reflexId, event.detail.payload)), true);
355
- const dispatchLifecycleEvent = (stage, reflexElement, controllerElement, reflexId, payload) => {
356
- if (!controllerElement) {
357
- if (Debug$1.enabled && !reflexes[reflexId].warned) {
358
- console.warn(`StimulusReflex was not able execute callbacks or emit events for "${stage}" or later life-cycle stages for this Reflex. The StimulusReflex Controller Element is no longer present in the DOM. Could you move the StimulusReflex Controller to an element higher in your DOM?`);
359
- reflexes[reflexId].warned = true;
360
- }
361
- return;
362
- }
363
- if (!controllerElement.reflexController || controllerElement.reflexController && !controllerElement.reflexController[reflexId]) {
364
- if (Debug$1.enabled && !reflexes[reflexId].warned) {
365
- console.warn(`StimulusReflex detected that the StimulusReflex Controller responsible for this Reflex has been replaced with a new instance. Callbacks and events for "${stage}" or later life-cycle stages cannot be executed.`);
366
- reflexes[reflexId].warned = true;
367
- }
368
- return;
369
- }
370
- const {target: target} = controllerElement.reflexData[reflexId] || {};
371
- const controller = controllerElement.reflexController[reflexId] || {};
372
- const event = `stimulus-reflex:${stage}`;
373
- const action = `${event}:${target.split("#")[1]}`;
374
- const detail = {
375
- reflex: target,
376
- controller: controller,
377
- reflexId: reflexId,
378
- element: reflexElement,
379
- payload: payload
380
- };
381
- const options = {
382
- bubbles: true,
383
- cancelable: false,
384
- detail: detail
385
- };
386
- controllerElement.dispatchEvent(new CustomEvent(event, options));
387
- controllerElement.dispatchEvent(new CustomEvent(action, options));
388
- if (window.jQuery) {
389
- window.jQuery(controllerElement).trigger(event, detail);
390
- window.jQuery(controllerElement).trigger(action, detail);
391
- }
895
+ var dependencies = {
896
+ "@hotwired/stimulus": "^3",
897
+ "@rails/actioncable": "^6 || ^7",
898
+ cable_ready: "5.0.0-rc1"
392
899
  };
393
- const localReflexControllers = (app, element) => attributeValues(element.getAttribute(Schema.controller)).reduce(((memo, name) => {
394
- const controller = app.getControllerForElementAndIdentifier(element, name);
395
- if (controller && controller.StimulusReflex) memo.push(controller);
396
- return memo;
397
- }), []);
398
- const allReflexControllers = (app, element) => {
399
- let controllers = [];
400
- while (element) {
401
- controllers = controllers.concat(localReflexControllers(app, element));
402
- element = element.parentElement;
403
- }
404
- return controllers;
900
+ var devDependencies = {
901
+ "@open-wc/testing": "^3.1.7",
902
+ "@rollup/plugin-json": "^6.0.0",
903
+ "@rollup/plugin-node-resolve": "^15.0.1",
904
+ "@rollup/plugin-terser": "^0.4.0",
905
+ "@web/dev-server-esbuild": "^0.3.3",
906
+ "@web/dev-server-rollup": "^0.3.21",
907
+ "@web/test-runner": "^0.15.1",
908
+ "prettier-standard": "^16.4.1",
909
+ rollup: "^3.19.1",
910
+ "toastify-js": "^1.12.0",
911
+ vitepress: "^1.0.0-alpha.56"
405
912
  };
406
- const findControllerByReflexName = (reflexName, controllers) => {
407
- const controller = controllers.find((controller => {
408
- if (!controller.identifier) return;
409
- return extractReflexName(reflexName).replace(/([a-z0–9])([A-Z])/g, "$1-$2").replace(/(::)/g, "--").toLowerCase() === controller.identifier;
410
- }));
411
- return controller || controllers[0];
913
+ var packageInfo = {
914
+ name: name,
915
+ version: version,
916
+ description: description,
917
+ keywords: keywords,
918
+ homepage: homepage,
919
+ bugs: bugs,
920
+ repository: repository,
921
+ license: license,
922
+ author: author,
923
+ contributors: contributors,
924
+ main: main,
925
+ module: module,
926
+ browser: browser,
927
+ import: "./dist/stimulus_reflex.js",
928
+ unpkg: unpkg,
929
+ umd: umd,
930
+ files: files,
931
+ scripts: scripts,
932
+ peerDependencies: peerDependencies,
933
+ dependencies: dependencies,
934
+ devDependencies: devDependencies
412
935
  };
413
- const received = data => {
414
- if (!data.cableReady) return;
415
- if (data.version.replace(".pre", "-pre") !== CableReady__default["default"].version) {
416
- if (Debug$1.enabled) console.error(`Reflex failed due to cable_ready gem/NPM package version mismatch. Package versions must match exactly.\nNote that if you are using pre-release builds, gems use the "x.y.z.preN" version format, while NPM packages use "x.y.z-preN".\n\ncable_ready gem: ${data.version}\ncable_ready NPM: ${CableReady__default["default"].version}`);
417
- return;
418
- }
419
- let reflexOperations = [];
420
- for (let i = data.operations.length - 1; i >= 0; i--) {
421
- if (data.operations[i].stimulusReflex) {
422
- reflexOperations.push(data.operations[i]);
423
- data.operations.splice(i, 1);
424
- }
425
- }
426
- if (reflexOperations.some((operation => operation.stimulusReflex.url !== location.href))) {
427
- return;
428
- }
429
- let reflexData;
430
- if (reflexOperations.length) {
431
- reflexData = reflexOperations[0].stimulusReflex;
432
- reflexData.payload = reflexOperations[0].payload;
433
- }
434
- if (reflexData) {
435
- const {reflexId: reflexId, payload: payload} = reflexData;
436
- if (!reflexes[reflexId] && IsolationMode.disabled) {
437
- const controllerElement = XPathToElement(reflexData.xpathController);
438
- const reflexElement = XPathToElement(reflexData.xpathElement);
439
- controllerElement.reflexController = controllerElement.reflexController || {};
440
- controllerElement.reflexData = controllerElement.reflexData || {};
441
- controllerElement.reflexError = controllerElement.reflexError || {};
442
- controllerElement.reflexController[reflexId] = reflexes.app.getControllerForElementAndIdentifier(controllerElement, reflexData.reflexController);
443
- controllerElement.reflexData[reflexId] = reflexData;
444
- dispatchLifecycleEvent("before", reflexElement, controllerElement, reflexId, payload);
445
- registerReflex(reflexData);
446
- }
447
- if (reflexes[reflexId]) {
448
- reflexes[reflexId].totalOperations = reflexOperations.length;
449
- reflexes[reflexId].pendingOperations = reflexOperations.length;
450
- reflexes[reflexId].completedOperations = 0;
451
- reflexes[reflexId].piggybackOperations = data.operations;
452
- CableReady__default["default"].perform(reflexOperations);
453
- }
454
- } else {
455
- if (data.operations.length && reflexes[data.operations[0].reflexId]) {
456
- CableReady__default["default"].perform(data.operations);
457
- }
458
- }
459
- };
460
- const registerReflex = data => {
461
- const {reflexId: reflexId} = data;
462
- reflexes[reflexId] = {
463
- finalStage: "finalize"
464
- };
465
- const promise = new Promise(((resolve, reject) => {
466
- reflexes[reflexId].promise = {
467
- resolve: resolve,
468
- reject: reject,
469
- data: data
470
- };
471
- }));
472
- promise.reflexId = reflexId;
473
- if (Debug$1.enabled) promise.catch((() => {}));
474
- return promise;
475
- };
476
- const getReflexRoots = element => {
477
- let list = [];
478
- while (list.length === 0 && element) {
479
- let reflexRoot = element.getAttribute(Schema.reflexRoot);
480
- if (reflexRoot) {
481
- if (reflexRoot.length === 0 && element.id) reflexRoot = `#${element.id}`;
482
- const selectors = reflexRoot.split(",").filter((s => s.trim().length));
483
- if (Debug$1.enabled && selectors.length === 0) {
484
- console.error(`No value found for ${Schema.reflexRoot}. Add an #id to the element or provide a value for ${Schema.reflexRoot}.`, element);
485
- }
486
- list = list.concat(selectors.filter((s => document.querySelector(s))));
487
- }
488
- element = element.parentElement ? element.parentElement.closest(`[${Schema.reflexRoot}]`) : null;
489
- }
490
- return list;
491
- };
492
- const setupDeclarativeReflexes = debounce((() => {
493
- document.querySelectorAll(`[${Schema.reflex}]`).forEach((element => {
494
- const controllers = attributeValues(element.getAttribute(Schema.controller));
495
- const reflexAttributeNames = attributeValues(element.getAttribute(Schema.reflex));
496
- const actions = attributeValues(element.getAttribute(Schema.action));
497
- reflexAttributeNames.forEach((reflexName => {
498
- const controller = findControllerByReflexName(reflexName, allReflexControllers(reflexes.app, element));
499
- let action;
500
- if (controller) {
501
- action = `${reflexName.split("->")[0]}->${controller.identifier}#__perform`;
502
- if (!actions.includes(action)) actions.push(action);
503
- } else {
504
- action = `${reflexName.split("->")[0]}->stimulus-reflex#__perform`;
505
- if (!controllers.includes("stimulus-reflex")) {
506
- controllers.push("stimulus-reflex");
507
- }
508
- if (!actions.includes(action)) actions.push(action);
509
- }
510
- }));
511
- const controllerValue = attributeValue(controllers);
512
- const actionValue = attributeValue(actions);
513
- if (controllerValue && element.getAttribute(Schema.controller) != controllerValue) {
514
- element.setAttribute(Schema.controller, controllerValue);
515
- }
516
- if (actionValue && element.getAttribute(Schema.action) != actionValue) element.setAttribute(Schema.action, actionValue);
517
- }));
518
- emitEvent("stimulus-reflex:ready");
519
- }), 20);
520
- var version = "3.5.0-pre9";
521
936
  class ReflexData {
522
937
  constructor(options, reflexElement, controllerElement, reflexController, permanentAttributeName, target, args, url, tabId) {
523
938
  this.options = options;
@@ -534,9 +949,9 @@
534
949
  this._attrs = this._attrs || this.options["attrs"] || extractElementAttributes(this.reflexElement);
535
950
  return this._attrs;
536
951
  }
537
- get reflexId() {
538
- this._reflexId = this._reflexId || this.options["reflexId"] || uuidv4();
539
- return this._reflexId;
952
+ get id() {
953
+ this._id = this._id || this.options["id"] || uuidv4();
954
+ return this._id;
540
955
  }
541
956
  get selectors() {
542
957
  this._selectors = this._selectors || this.options["selectors"] || getReflexRoots(this.reflexElement);
@@ -581,7 +996,7 @@
581
996
  attrs: this.attrs,
582
997
  dataset: this.dataset,
583
998
  selectors: this.selectors,
584
- reflexId: this.reflexId,
999
+ id: this.id,
585
1000
  resolveLate: this.resolveLate,
586
1001
  suppressLogging: this.suppressLogging,
587
1002
  xpathController: this.xpathController,
@@ -595,198 +1010,207 @@
595
1010
  args: this.args,
596
1011
  url: this.url,
597
1012
  tabId: this.tabId,
598
- version: version
1013
+ version: packageInfo.version
599
1014
  };
600
1015
  }
601
1016
  }
602
- let consumer;
603
- let params;
604
- let subscriptionActive;
605
- const createSubscription = controller => {
606
- consumer = consumer || controller.application.consumer || actioncable.createConsumer();
607
- const {channel: channel} = controller.StimulusReflex;
608
- const subscription = {
609
- channel: channel,
610
- ...params
611
- };
612
- const identifier = JSON.stringify(subscription);
613
- controller.StimulusReflex.subscription = consumer.subscriptions.findAll(identifier)[0] || consumer.subscriptions.create(subscription, {
614
- received: received,
615
- connected: connected,
616
- rejected: rejected,
617
- disconnected: disconnected
618
- });
619
- };
620
- const connected = () => {
621
- subscriptionActive = true;
622
- document.body.classList.replace("stimulus-reflex-disconnected", "stimulus-reflex-connected");
623
- emitEvent("stimulus-reflex:connected");
624
- emitEvent("stimulus-reflex:action-cable:connected");
625
- };
626
- const rejected = () => {
627
- subscriptionActive = false;
628
- document.body.classList.replace("stimulus-reflex-connected", "stimulus-reflex-disconnected");
629
- emitEvent("stimulus-reflex:rejected");
630
- emitEvent("stimulus-reflex:action-cable:rejected");
631
- if (Debug.enabled) console.warn("Channel subscription was rejected.");
632
- };
633
- const disconnected = willAttemptReconnect => {
634
- subscriptionActive = false;
635
- document.body.classList.replace("stimulus-reflex-connected", "stimulus-reflex-disconnected");
636
- emitEvent("stimulus-reflex:disconnected", willAttemptReconnect);
637
- emitEvent("stimulus-reflex:action-cable:disconnected", willAttemptReconnect);
638
- };
639
- var ActionCableTransport = {
640
- consumer: consumer,
641
- params: params,
642
- get subscriptionActive() {
643
- return subscriptionActive;
1017
+ let transport = {};
1018
+ var Transport = {
1019
+ get plugin() {
1020
+ return transport;
644
1021
  },
645
- createSubscription: createSubscription,
646
- connected: connected,
647
- rejected: rejected,
648
- disconnected: disconnected,
649
- set(consumerValue, paramsValue) {
650
- consumer = consumerValue;
651
- params = paramsValue;
1022
+ set(newTransport) {
1023
+ transport = newTransport;
652
1024
  }
653
1025
  };
654
1026
  const beforeDOMUpdate = event => {
655
- const {stimulusReflex: stimulusReflex, payload: payload} = event.detail || {};
1027
+ const {stimulusReflex: stimulusReflex} = event.detail || {};
656
1028
  if (!stimulusReflex) return;
657
- const {reflexId: reflexId, xpathElement: xpathElement, xpathController: xpathController} = stimulusReflex;
658
- const controllerElement = XPathToElement(xpathController);
659
- const reflexElement = XPathToElement(xpathElement);
660
- const reflex = reflexes[reflexId];
661
- const {promise: promise} = reflex;
1029
+ const reflex = reflexes[stimulusReflex.id];
662
1030
  reflex.pendingOperations--;
663
1031
  if (reflex.pendingOperations > 0) return;
664
- if (!stimulusReflex.resolveLate) setTimeout((() => promise.resolve({
665
- element: reflexElement,
1032
+ if (!stimulusReflex.resolveLate) setTimeout((() => reflex.promise.resolve({
1033
+ element: reflex.element,
666
1034
  event: event,
667
- data: promise.data,
668
- payload: payload,
669
- reflexId: reflexId,
1035
+ data: reflex.data,
1036
+ payload: reflex.payload,
1037
+ id: reflex.id,
670
1038
  toString: () => ""
671
1039
  })));
672
- setTimeout((() => dispatchLifecycleEvent("success", reflexElement, controllerElement, reflexId, payload)));
1040
+ setTimeout((() => dispatchLifecycleEvent(reflex, "success")));
673
1041
  };
674
1042
  const afterDOMUpdate = event => {
675
- const {stimulusReflex: stimulusReflex, payload: payload} = event.detail || {};
1043
+ const {stimulusReflex: stimulusReflex} = event.detail || {};
676
1044
  if (!stimulusReflex) return;
677
- const {reflexId: reflexId, xpathElement: xpathElement, xpathController: xpathController} = stimulusReflex;
678
- const controllerElement = XPathToElement(xpathController);
679
- const reflexElement = XPathToElement(xpathElement);
680
- const reflex = reflexes[reflexId];
681
- const {promise: promise} = reflex;
1045
+ const reflex = reflexes[stimulusReflex.id];
682
1046
  reflex.completedOperations++;
683
- Log.success(event, false);
1047
+ reflex.selector = event.detail.selector;
1048
+ reflex.morph = event.detail.stimulusReflex.morph;
1049
+ reflex.operation = event.type.split(":")[1].split("-").slice(1).join("_");
1050
+ Log.success(reflex);
684
1051
  if (reflex.completedOperations < reflex.totalOperations) return;
685
- if (stimulusReflex.resolveLate) setTimeout((() => promise.resolve({
686
- element: reflexElement,
1052
+ if (stimulusReflex.resolveLate) setTimeout((() => reflex.promise.resolve({
1053
+ element: reflex.element,
687
1054
  event: event,
688
- data: promise.data,
689
- payload: payload,
690
- reflexId: reflexId,
1055
+ data: reflex.data,
1056
+ payload: reflex.payload,
1057
+ id: reflex.id,
691
1058
  toString: () => ""
692
1059
  })));
693
- setTimeout((() => dispatchLifecycleEvent("finalize", reflexElement, controllerElement, reflexId, payload)));
694
- if (reflex.piggybackOperations.length) CableReady__default["default"].perform(reflex.piggybackOperations);
1060
+ setTimeout((() => dispatchLifecycleEvent(reflex, "finalize")));
1061
+ if (reflex.piggybackOperations.length) CableReady.perform(reflex.piggybackOperations);
695
1062
  };
696
1063
  const routeReflexEvent = event => {
697
- const {stimulusReflex: stimulusReflex, payload: payload, name: name, body: body} = event.detail || {};
1064
+ const {stimulusReflex: stimulusReflex, name: name} = event.detail || {};
698
1065
  const eventType = name.split("-")[2];
699
- if (!stimulusReflex || ![ "nothing", "halted", "error" ].includes(eventType)) return;
700
- const {reflexId: reflexId, xpathElement: xpathElement, xpathController: xpathController} = stimulusReflex;
701
- const reflexElement = XPathToElement(xpathElement);
702
- const controllerElement = XPathToElement(xpathController);
703
- const reflex = reflexes[reflexId];
704
- const {promise: promise} = reflex;
705
- if (controllerElement) {
706
- controllerElement.reflexError = controllerElement.reflexError || {};
707
- if (eventType === "error") controllerElement.reflexError[reflexId] = body;
708
- }
709
- switch (eventType) {
710
- case "nothing":
711
- nothing(event, payload, promise, reflex, reflexElement);
712
- break;
713
-
714
- case "error":
715
- error(event, payload, promise, reflex, reflexElement);
716
- break;
717
-
718
- case "halted":
719
- halted(event, payload, promise, reflex, reflexElement);
720
- break;
721
- }
722
- setTimeout((() => dispatchLifecycleEvent(eventType, reflexElement, controllerElement, reflexId, payload)));
723
- if (reflex.piggybackOperations.length) CableReady__default["default"].perform(reflex.piggybackOperations);
724
- };
725
- const nothing = (event, payload, promise, reflex, reflexElement) => {
726
- reflex.finalStage = "after";
727
- Log.success(event, false);
728
- setTimeout((() => promise.resolve({
729
- data: promise.data,
730
- element: reflexElement,
1066
+ const eventTypes = {
1067
+ nothing: nothing,
1068
+ halted: halted,
1069
+ forbidden: forbidden,
1070
+ error: error
1071
+ };
1072
+ if (!stimulusReflex || !Object.keys(eventTypes).includes(eventType)) return;
1073
+ const reflex = reflexes[stimulusReflex.id];
1074
+ reflex.completedOperations++;
1075
+ reflex.pendingOperations--;
1076
+ reflex.selector = event.detail.selector;
1077
+ reflex.morph = event.detail.stimulusReflex.morph;
1078
+ reflex.operation = event.type.split(":")[1].split("-").slice(1).join("_");
1079
+ if (eventType === "error") reflex.error = event.detail.error;
1080
+ eventTypes[eventType](reflex, event);
1081
+ setTimeout((() => dispatchLifecycleEvent(reflex, eventType)));
1082
+ if (reflex.piggybackOperations.length) CableReady.perform(reflex.piggybackOperations);
1083
+ };
1084
+ const nothing = (reflex, event) => {
1085
+ Log.success(reflex);
1086
+ setTimeout((() => reflex.promise.resolve({
1087
+ data: reflex.data,
1088
+ element: reflex.element,
1089
+ event: event,
1090
+ payload: reflex.payload,
1091
+ id: reflex.id,
1092
+ toString: () => ""
1093
+ })));
1094
+ };
1095
+ const halted = (reflex, event) => {
1096
+ Log.halted(reflex, event);
1097
+ setTimeout((() => reflex.promise.resolve({
1098
+ data: reflex.data,
1099
+ element: reflex.element,
731
1100
  event: event,
732
- payload: payload,
733
- reflexId: promise.data.reflexId,
1101
+ payload: reflex.payload,
1102
+ id: reflex.id,
734
1103
  toString: () => ""
735
1104
  })));
736
1105
  };
737
- const halted = (event, payload, promise, reflex, reflexElement) => {
738
- reflex.finalStage = "halted";
739
- Log.success(event, true);
740
- setTimeout((() => promise.resolve({
741
- data: promise.data,
742
- element: reflexElement,
1106
+ const forbidden = (reflex, event) => {
1107
+ Log.forbidden(reflex, event);
1108
+ setTimeout((() => reflex.promise.resolve({
1109
+ data: reflex.data,
1110
+ element: reflex.element,
743
1111
  event: event,
744
- payload: payload,
745
- reflexId: promise.data.reflexId,
1112
+ payload: reflex.payload,
1113
+ id: reflex.id,
746
1114
  toString: () => ""
747
1115
  })));
748
1116
  };
749
- const error = (event, payload, promise, reflex, reflexElement) => {
750
- reflex.finalStage = "after";
751
- Log.error(event);
752
- setTimeout((() => promise.reject({
753
- data: promise.data,
754
- element: reflexElement,
1117
+ const error = (reflex, event) => {
1118
+ Log.error(reflex, event);
1119
+ setTimeout((() => reflex.promise.reject({
1120
+ data: reflex.data,
1121
+ element: reflex.element,
755
1122
  event: event,
756
- payload: payload,
757
- reflexId: promise.data.reflexId,
758
- error: event.detail.body,
759
- toString: () => event.detail.body
1123
+ payload: reflex.payload,
1124
+ id: reflex.id,
1125
+ error: reflex.error,
1126
+ toString: () => reflex.error
760
1127
  })));
761
1128
  };
1129
+ const localReflexControllers = element => {
1130
+ const potentialIdentifiers = attributeValues(element.getAttribute(Schema.controller));
1131
+ const potentialControllers = potentialIdentifiers.map((identifier => App.app.getControllerForElementAndIdentifier(element, identifier)));
1132
+ return potentialControllers.filter((controller => controller && controller.StimulusReflex));
1133
+ };
1134
+ const allReflexControllers = element => {
1135
+ let controllers = [];
1136
+ while (element) {
1137
+ controllers = controllers.concat(localReflexControllers(element));
1138
+ element = element.parentElement;
1139
+ }
1140
+ return controllers;
1141
+ };
1142
+ const findControllerByReflexName = (reflexName, controllers) => {
1143
+ const controller = controllers.find((controller => {
1144
+ if (!controller || !controller.identifier) return;
1145
+ const identifier = reflexNameToControllerIdentifier(extractReflexName(reflexName));
1146
+ return identifier === controller.identifier;
1147
+ }));
1148
+ return controller || controllers[0];
1149
+ };
1150
+ const scanForReflexes = debounce((() => {
1151
+ const reflexElements = document.querySelectorAll(`[${Schema.reflex}]`);
1152
+ reflexElements.forEach((element => scanForReflexesOnElement(element)));
1153
+ }), 20);
1154
+ const scanForReflexesOnElement = (element, controller = null) => {
1155
+ const controllerAttribute = element.getAttribute(Schema.controller);
1156
+ const controllers = attributeValues(controllerAttribute).filter((controller => controller !== "stimulus-reflex"));
1157
+ const reflexAttribute = element.getAttribute(Schema.reflex);
1158
+ const reflexAttributeNames = attributeValues(reflexAttribute);
1159
+ const actionAttribute = element.getAttribute(Schema.action);
1160
+ const actions = attributeValues(actionAttribute).filter((action => !action.includes("#__perform")));
1161
+ reflexAttributeNames.forEach((reflexName => {
1162
+ const potentialControllers = [ controller ].concat(allReflexControllers(element));
1163
+ controller = findControllerByReflexName(reflexName, potentialControllers);
1164
+ const controllerName = controller ? controller.identifier : "stimulus-reflex";
1165
+ actions.push(`${reflexName.split("->")[0]}->${controllerName}#__perform`);
1166
+ const parentControllerElement = element.closest(`[data-controller~=${controllerName}]`);
1167
+ if (!parentControllerElement) {
1168
+ controllers.push(controllerName);
1169
+ }
1170
+ }));
1171
+ const controllerValue = attributeValue(controllers);
1172
+ const actionValue = attributeValue(actions);
1173
+ let emitReadyEvent = false;
1174
+ if (controllerValue && element.getAttribute(Schema.controller) != controllerValue) {
1175
+ element.setAttribute(Schema.controller, controllerValue);
1176
+ emitReadyEvent = true;
1177
+ }
1178
+ if (actionValue && element.getAttribute(Schema.action) != actionValue) {
1179
+ element.setAttribute(Schema.action, actionValue);
1180
+ emitReadyEvent = true;
1181
+ }
1182
+ if (emitReadyEvent) {
1183
+ dispatch(element, "stimulus-reflex:ready", {
1184
+ reflex: reflexAttribute,
1185
+ controller: controllerValue,
1186
+ action: actionValue,
1187
+ element: element
1188
+ });
1189
+ }
1190
+ };
762
1191
  class StimulusReflexController extends stimulus.Controller {
763
1192
  constructor(...args) {
764
1193
  super(...args);
765
1194
  register(this);
766
1195
  }
767
1196
  }
768
- const initialize = (application, {controller: controller, consumer: consumer, debug: debug, params: params, isolate: isolate, deprecate: deprecate} = {}) => {
769
- ActionCableTransport.set(consumer, params);
770
- document.addEventListener("DOMContentLoaded", (() => {
771
- document.body.classList.remove("stimulus-reflex-connected");
772
- document.body.classList.add("stimulus-reflex-disconnected");
773
- if (Deprecate.enabled && consumer) console.warn("Deprecation warning: the next version of StimulusReflex will obtain a reference to consumer via the Stimulus application object.\nPlease add 'application.consumer = consumer' to your index.js after your Stimulus application has been established, and remove the consumer key from your StimulusReflex initialize() options object.");
774
- if (Deprecate.enabled && IsolationMode.disabled) console.warn("Deprecation warning: the next version of StimulusReflex will standardize isolation mode, and the isolate option will be removed.\nPlease update your applications to assume that every tab will be isolated.");
775
- }), {
776
- once: true
777
- });
1197
+ const tabId = uuidv4();
1198
+ const initialize = (application, {controller: controller, consumer: consumer, debug: debug, params: params, isolate: isolate, deprecate: deprecate, transport: transport} = {}) => {
1199
+ Transport.set(transport || ActionCableTransport);
1200
+ Transport.plugin.initialize(consumer, params);
778
1201
  IsolationMode.set(!!isolate);
779
- reflexes.app = application;
1202
+ App.set(application);
780
1203
  Schema.set(application);
781
- reflexes.app.register("stimulus-reflex", controller || StimulusReflexController);
1204
+ App.app.register("stimulus-reflex", controller || StimulusReflexController);
782
1205
  Debug$1.set(!!debug);
783
1206
  if (typeof deprecate !== "undefined") Deprecate.set(deprecate);
784
- const observer = new MutationObserver(setupDeclarativeReflexes);
1207
+ const observer = new MutationObserver(scanForReflexes);
785
1208
  observer.observe(document.documentElement, {
786
1209
  attributeFilter: [ Schema.reflex, Schema.action ],
787
1210
  childList: true,
788
1211
  subtree: true
789
1212
  });
1213
+ emitEvent("stimulus-reflex:initialized");
790
1214
  };
791
1215
  const register = (controller, options = {}) => {
792
1216
  const channel = "StimulusReflex::Channel";
@@ -794,38 +1218,32 @@
794
1218
  ...options,
795
1219
  channel: channel
796
1220
  };
797
- ActionCableTransport.createSubscription(controller);
1221
+ Transport.plugin.subscribe(controller);
798
1222
  Object.assign(controller, {
799
- isActionCableConnectionOpen() {
800
- return this.StimulusReflex.subscription.consumer.connection.isOpen();
801
- },
802
1223
  stimulate() {
803
1224
  const url = location.href;
1225
+ const controllerElement = this.element;
804
1226
  const args = Array.from(arguments);
805
1227
  const target = args.shift() || "StimulusReflex::Reflex#default_reflex";
806
- const controllerElement = this.element;
807
- const reflexElement = args[0] && args[0].nodeType === Node.ELEMENT_NODE ? args.shift() : controllerElement;
808
- if (reflexElement.type === "number" && reflexElement.validity && reflexElement.validity.badInput) {
1228
+ const reflexElement = getReflexElement(args, controllerElement);
1229
+ if (elementInvalid(reflexElement)) {
809
1230
  if (Debug$1.enabled) console.warn("Reflex aborted: invalid numeric input");
810
1231
  return;
811
1232
  }
812
- const options = {};
813
- if (args[0] && typeof args[0] === "object" && Object.keys(args[0]).filter((key => [ "attrs", "selectors", "reflexId", "resolveLate", "serializeForm", "suppressLogging", "includeInnerHTML", "includeTextContent" ].includes(key))).length) {
814
- const opts = args.shift();
815
- Object.keys(opts).forEach((o => options[o] = opts[o]));
816
- }
1233
+ const options = getReflexOptions(args);
817
1234
  const reflexData = new ReflexData(options, reflexElement, controllerElement, this.identifier, Schema.reflexPermanent, target, args, url, tabId);
818
- const reflexId = reflexData.reflexId;
819
- if (!this.isActionCableConnectionOpen()) throw "The ActionCable connection is not open! `this.isActionCableConnectionOpen()` must return true before calling `this.stimulate()`";
820
- if (!ActionCableTransport.subscriptionActive) throw "The ActionCable channel subscription for StimulusReflex was rejected.";
1235
+ const id = reflexData.id;
821
1236
  controllerElement.reflexController = controllerElement.reflexController || {};
822
1237
  controllerElement.reflexData = controllerElement.reflexData || {};
823
1238
  controllerElement.reflexError = controllerElement.reflexError || {};
824
- controllerElement.reflexController[reflexId] = this;
825
- controllerElement.reflexData[reflexId] = reflexData.valueOf();
826
- dispatchLifecycleEvent("before", reflexElement, controllerElement, reflexId);
1239
+ controllerElement.reflexController[id] = this;
1240
+ controllerElement.reflexData[id] = reflexData.valueOf();
1241
+ const reflex = new Reflex(reflexData, this);
1242
+ reflexes[id] = reflex;
1243
+ this.lastReflex = reflex;
1244
+ dispatchLifecycleEvent(reflex, "before");
827
1245
  setTimeout((() => {
828
- const {params: params} = controllerElement.reflexData[reflexId] || {};
1246
+ const {params: params} = controllerElement.reflexData[id] || {};
829
1247
  const check = reflexElement.attributes[Schema.reflexSerializeForm];
830
1248
  if (check) {
831
1249
  options["serializeForm"] = check.value !== "false";
@@ -835,16 +1253,16 @@
835
1253
  const formData = options["serializeForm"] === false ? "" : serializeForm(form, {
836
1254
  element: reflexElement
837
1255
  });
838
- controllerElement.reflexData[reflexId] = {
1256
+ reflex.data = {
839
1257
  ...reflexData.valueOf(),
840
1258
  params: params,
841
1259
  formData: formData
842
1260
  };
843
- this.StimulusReflex.subscription.send(controllerElement.reflexData[reflexId]);
1261
+ controllerElement.reflexData[id] = reflex.data;
1262
+ Transport.plugin.deliver(reflex);
844
1263
  }));
845
- const promise = registerReflex(reflexData.valueOf());
846
- Log.request(reflexId, target, args, this.context.scope.identifier, reflexElement, controllerElement);
847
- return promise;
1264
+ Log.request(reflex);
1265
+ return reflex.getPromise;
848
1266
  },
849
1267
  __perform(event) {
850
1268
  let element = event.target;
@@ -861,8 +1279,23 @@
861
1279
  }
862
1280
  }
863
1281
  });
1282
+ if (!controller.reflexes) Object.defineProperty(controller, "reflexes", {
1283
+ get() {
1284
+ return new Proxy(reflexes, {
1285
+ get: function(target, prop) {
1286
+ if (prop === "last") return this.lastReflex;
1287
+ return Object.fromEntries(Object.entries(target[prop]).filter((([_, reflex]) => reflex.controller === this)));
1288
+ }.bind(this)
1289
+ });
1290
+ }
1291
+ });
1292
+ scanForReflexesOnElement(controller.element, controller);
1293
+ emitEvent("stimulus-reflex:controller-registered", {
1294
+ detail: {
1295
+ controller: controller
1296
+ }
1297
+ });
864
1298
  };
865
- const tabId = uuidv4();
866
1299
  const useReflex = (controller, options = {}) => {
867
1300
  register(controller, options);
868
1301
  };
@@ -871,14 +1304,22 @@
871
1304
  document.addEventListener("cable-ready:before-morph", beforeDOMUpdate);
872
1305
  document.addEventListener("cable-ready:after-inner-html", afterDOMUpdate);
873
1306
  document.addEventListener("cable-ready:after-morph", afterDOMUpdate);
874
- window.addEventListener("load", setupDeclarativeReflexes);
1307
+ document.addEventListener("readystatechange", (() => {
1308
+ if (document.readyState === "complete") {
1309
+ scanForReflexes();
1310
+ }
1311
+ }));
875
1312
  var StimulusReflex = Object.freeze({
876
1313
  __proto__: null,
877
1314
  initialize: initialize,
1315
+ reflexes: reflexes,
878
1316
  register: register,
1317
+ scanForReflexes: scanForReflexes,
1318
+ scanForReflexesOnElement: scanForReflexesOnElement,
879
1319
  useReflex: useReflex
880
1320
  });
881
1321
  const global = {
1322
+ version: packageInfo.version,
882
1323
  ...StimulusReflex,
883
1324
  get debug() {
884
1325
  return Debug$1.value;
@@ -894,9 +1335,12 @@
894
1335
  }
895
1336
  };
896
1337
  window.StimulusReflex = global;
897
- exports["default"] = global;
1338
+ exports.default = global;
898
1339
  exports.initialize = initialize;
1340
+ exports.reflexes = reflexes;
899
1341
  exports.register = register;
1342
+ exports.scanForReflexes = scanForReflexes;
1343
+ exports.scanForReflexesOnElement = scanForReflexesOnElement;
900
1344
  exports.useReflex = useReflex;
901
1345
  Object.defineProperty(exports, "__esModule", {
902
1346
  value: true