stimulus_reflex 3.5.0.pre9 → 3.5.0.pre10

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +122 -127
  4. data/README.md +10 -16
  5. data/app/assets/javascripts/stimulus_reflex.js +710 -505
  6. data/app/assets/javascripts/stimulus_reflex.min.js +1 -1
  7. data/app/assets/javascripts/stimulus_reflex.min.js.map +1 -1
  8. data/app/assets/javascripts/stimulus_reflex.umd.js +660 -500
  9. data/app/assets/javascripts/stimulus_reflex.umd.min.js +660 -500
  10. data/app/assets/javascripts/stimulus_reflex.umd.min.js.map +1 -1
  11. data/app/channels/stimulus_reflex/channel.rb +9 -7
  12. data/bin/console +0 -2
  13. data/bin/standardize +2 -1
  14. data/lib/generators/stimulus_reflex/stimulus_reflex_generator.rb +72 -7
  15. data/lib/generators/stimulus_reflex/templates/app/controllers/examples_controller.rb.tt +9 -0
  16. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/consumer.js.tt +6 -0
  17. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.esbuild.tt +4 -0
  18. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.importmap.tt +2 -0
  19. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.shakapacker.tt +5 -0
  20. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.vite.tt +1 -0
  21. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.webpacker.tt +5 -0
  22. data/lib/generators/stimulus_reflex/templates/app/javascript/config/cable_ready.js.tt +4 -0
  23. data/lib/generators/stimulus_reflex/templates/app/javascript/config/index.js.tt +2 -0
  24. data/lib/generators/stimulus_reflex/templates/app/javascript/config/mrujs.js.tt +9 -0
  25. data/lib/generators/stimulus_reflex/templates/app/javascript/config/stimulus_reflex.js.tt +5 -0
  26. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/%file_name%_controller.js.tt +141 -0
  27. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application.js.tt +11 -0
  28. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application_controller.js.tt +74 -0
  29. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.esbuild.tt +7 -0
  30. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.importmap.tt +5 -0
  31. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.shakapacker.tt +5 -0
  32. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.vite.tt +5 -0
  33. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.webpacker.tt +5 -0
  34. data/{test/tmp/app/reflexes/user_reflex.rb → lib/generators/stimulus_reflex/templates/app/reflexes/%file_name%_reflex.rb.tt} +38 -9
  35. data/lib/generators/stimulus_reflex/templates/app/reflexes/application_reflex.rb.tt +27 -0
  36. data/lib/generators/stimulus_reflex/templates/app/views/examples/show.html.erb.tt +207 -0
  37. data/lib/generators/stimulus_reflex/templates/config/initializers/cable_ready.rb +22 -0
  38. data/lib/generators/stimulus_reflex/templates/config/initializers/stimulus_reflex.rb +18 -13
  39. data/lib/generators/stimulus_reflex/templates/esbuild.config.mjs.tt +94 -0
  40. data/lib/install/action_cable.rb +155 -0
  41. data/lib/install/broadcaster.rb +90 -0
  42. data/lib/install/bundle.rb +56 -0
  43. data/lib/install/compression.rb +41 -0
  44. data/lib/install/config.rb +87 -0
  45. data/lib/install/development.rb +110 -0
  46. data/lib/install/esbuild.rb +114 -0
  47. data/lib/install/example.rb +22 -0
  48. data/lib/install/importmap.rb +133 -0
  49. data/lib/install/initializers.rb +25 -0
  50. data/lib/install/mrujs.rb +133 -0
  51. data/lib/install/npm_packages.rb +25 -0
  52. data/lib/install/reflexes.rb +25 -0
  53. data/lib/install/shakapacker.rb +64 -0
  54. data/lib/install/spring.rb +54 -0
  55. data/lib/install/updatable.rb +34 -0
  56. data/lib/install/vite.rb +64 -0
  57. data/lib/install/webpacker.rb +90 -0
  58. data/lib/install/yarn.rb +55 -0
  59. data/lib/stimulus_reflex/broadcasters/broadcaster.rb +15 -8
  60. data/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +7 -8
  61. data/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +10 -10
  62. data/lib/stimulus_reflex/broadcasters/update.rb +3 -0
  63. data/lib/stimulus_reflex/cable_readiness.rb +29 -0
  64. data/lib/stimulus_reflex/cable_ready_channels.rb +5 -5
  65. data/lib/stimulus_reflex/callbacks.rb +17 -1
  66. data/lib/stimulus_reflex/concern_enhancer.rb +6 -4
  67. data/lib/stimulus_reflex/configuration.rb +12 -2
  68. data/lib/stimulus_reflex/dataset.rb +11 -1
  69. data/lib/stimulus_reflex/engine.rb +20 -9
  70. data/lib/stimulus_reflex/html/document.rb +59 -0
  71. data/lib/stimulus_reflex/html/document_fragment.rb +13 -0
  72. data/lib/stimulus_reflex/importmap.rb +3 -0
  73. data/lib/stimulus_reflex/installer.rb +274 -0
  74. data/lib/stimulus_reflex/open_struct_fix.rb +2 -0
  75. data/lib/stimulus_reflex/reflex.rb +25 -25
  76. data/lib/stimulus_reflex/reflex_data.rb +15 -3
  77. data/lib/stimulus_reflex/reflex_factory.rb +4 -2
  78. data/lib/stimulus_reflex/request_parameters.rb +2 -0
  79. data/lib/stimulus_reflex/utils/logger.rb +10 -0
  80. data/lib/stimulus_reflex/utils/sanity_checker.rb +8 -48
  81. data/lib/stimulus_reflex/version.rb +1 -1
  82. data/lib/stimulus_reflex.rb +2 -0
  83. data/lib/tasks/stimulus_reflex/stimulus_reflex.rake +252 -0
  84. data/package.json +34 -28
  85. data/{rollup.config.js → rollup.config.mjs} +8 -7
  86. data/stimulus_reflex.gemspec +16 -19
  87. data/yarn.lock +1320 -742
  88. metadata +124 -77
  89. data/LATEST +0 -1
  90. data/lib/generators/stimulus_reflex/initializer_generator.rb +0 -14
  91. data/test/broadcasters/broadcaster_test.rb +0 -11
  92. data/test/broadcasters/broadcaster_test_case.rb +0 -39
  93. data/test/broadcasters/nothing_broadcaster_test.rb +0 -31
  94. data/test/broadcasters/page_broadcaster_test.rb +0 -79
  95. data/test/broadcasters/selector_broadcaster_test.rb +0 -173
  96. data/test/callbacks_test.rb +0 -652
  97. data/test/concern_enhancer_test.rb +0 -54
  98. data/test/element_test.rb +0 -254
  99. data/test/generators/stimulus_reflex_generator_test.rb +0 -58
  100. data/test/reflex_test.rb +0 -43
  101. data/test/test_helper.rb +0 -71
  102. data/test/tmp/app/reflexes/application_reflex.rb +0 -12
  103. data/yarn-error.log +0 -4964
@@ -1,14 +1,44 @@
1
1
  (function(global, factory) {
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));
4
- })(this, (function(exports, stimulus, CableReady, actioncable) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@hotwired/stimulus"), require("@rails/actioncable"), require("cable_ready")) : typeof define === "function" && define.amd ? define([ "exports", "@hotwired/stimulus", "@rails/actioncable", "cable_ready" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
3
+ factory(global.StimulusReflex = {}, global.Stimulus, global.ActionCable, global.CableReady));
4
+ })(this, (function(exports, stimulus, actioncable, CableReady) {
5
5
  "use strict";
6
- function _interopDefaultLegacy(e) {
7
- return e && typeof e === "object" && "default" in e ? e : {
8
- default: e
9
- };
10
- }
11
- var CableReady__default = _interopDefaultLegacy(CableReady);
6
+ let deprecationWarnings = true;
7
+ var Deprecate = {
8
+ get enabled() {
9
+ return deprecationWarnings;
10
+ },
11
+ get disabled() {
12
+ return !deprecationWarnings;
13
+ },
14
+ get value() {
15
+ return deprecationWarnings;
16
+ },
17
+ set(value) {
18
+ deprecationWarnings = !!value;
19
+ },
20
+ set deprecate(value) {
21
+ deprecationWarnings = !!value;
22
+ }
23
+ };
24
+ let debugging = false;
25
+ var Debug$1 = {
26
+ get enabled() {
27
+ return debugging;
28
+ },
29
+ get disabled() {
30
+ return !debugging;
31
+ },
32
+ get value() {
33
+ return debugging;
34
+ },
35
+ set(value) {
36
+ debugging = !!value;
37
+ },
38
+ set debug(value) {
39
+ debugging = !!value;
40
+ }
41
+ };
12
42
  const defaultSchema = {
13
43
  reflexAttribute: "data-reflex",
14
44
  reflexPermanentAttribute: "data-reflex-permanent",
@@ -29,94 +59,15 @@
29
59
  ...application.schema
30
60
  };
31
61
  for (const attribute in schema) {
32
- Object.defineProperty(this, attribute.slice(0, -9), {
33
- get: () => schema[attribute]
62
+ const attributeName = attribute.slice(0, -9);
63
+ Object.defineProperty(this, attributeName, {
64
+ get: () => schema[attribute],
65
+ configurable: true
34
66
  });
35
67
  }
36
68
  }
37
69
  };
38
- let debugging = false;
39
- var Debug$1 = {
40
- get enabled() {
41
- return debugging;
42
- },
43
- get disabled() {
44
- return !debugging;
45
- },
46
- get value() {
47
- return debugging;
48
- },
49
- set(value) {
50
- debugging = !!value;
51
- },
52
- set debug(value) {
53
- debugging = !!value;
54
- }
55
- };
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
101
- };
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;
118
- }
119
- };
70
+ const {debounce: debounce, dispatch: dispatch, xpathToElement: xpathToElement, xpathToElementArray: xpathToElementArray} = CableReady.Utils;
120
71
  const uuidv4 = () => {
121
72
  const crypto = window.crypto || window.msCrypto;
122
73
  return ([ 1e7 ] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)));
@@ -141,33 +92,19 @@
141
92
  if (uppercaseFirstLetter) value = value.substr(0, 1).toUpperCase() + value.substr(1);
142
93
  return value;
143
94
  };
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
- };
95
+ const XPathToElement = xpathToElement;
96
+ const XPathToArray = xpathToElementArray;
97
+ const emitEvent = (name, detail = {}) => dispatch(document, name, detail);
154
98
  const extractReflexName = reflexString => {
155
99
  const match = reflexString.match(/(?:.*->)?(.*?)(?:Reflex)?#/);
156
100
  return match ? match[1] : "";
157
101
  };
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
102
  const elementToXPath = element => {
167
103
  if (element.id !== "") return "//*[@id='" + element.id + "']";
168
104
  if (element === document.body) return "/html/body";
105
+ if (element.nodeName === "HTML") return "/html";
169
106
  let ix = 0;
170
- const siblings = element?.parentNode ? element.parentNode.childNodes : [];
107
+ const siblings = element && element.parentNode ? element.parentNode.childNodes : [];
171
108
  for (var i = 0; i < siblings.length; i++) {
172
109
  const sibling = siblings[i];
173
110
  if (sibling === element) {
@@ -181,14 +118,354 @@
181
118
  }
182
119
  }
183
120
  };
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));
121
+ const elementInvalid = element => element.type === "number" && element.validity && element.validity.badInput;
122
+ const getReflexElement = (args, element) => args[0] && args[0].nodeType === Node.ELEMENT_NODE ? args.shift() : element;
123
+ const getReflexOptions = args => {
124
+ const options = {};
125
+ 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) {
126
+ const opts = args.shift();
127
+ Object.keys(opts).forEach((o => {
128
+ if (o === "reflexId") {
129
+ if (Deprecate.enabled) console.warn("reflexId option will be removed in v4. Use id instead.");
130
+ options["id"] = opts["reflexId"];
131
+ } else options[o] = opts[o];
132
+ }));
133
+ }
134
+ return options;
135
+ };
136
+ const getReflexRoots = element => {
137
+ let list = [];
138
+ while (list.length === 0 && element) {
139
+ let reflexRoot = element.getAttribute(Schema.reflexRoot);
140
+ if (reflexRoot) {
141
+ if (reflexRoot.length === 0 && element.id) reflexRoot = `#${element.id}`;
142
+ const selectors = reflexRoot.split(",").filter((s => s.trim().length));
143
+ if (Debug$1.enabled && selectors.length === 0) {
144
+ console.error(`No value found for ${Schema.reflexRoot}. Add an #id to the element or provide a value for ${Schema.reflexRoot}.`, element);
145
+ }
146
+ list = list.concat(selectors.filter((s => document.querySelector(s))));
147
+ }
148
+ element = element.parentElement ? element.parentElement.closest(`[${Schema.reflexRoot}]`) : null;
149
+ }
150
+ return list;
151
+ };
152
+ const stages = [ "created", "before", "delivered", "queued", "after", "finalized", "success", "error", "halted", "forbidden" ];
153
+ let lastReflex;
154
+ const reflexes = new Proxy({}, {
155
+ get: function(target, prop) {
156
+ 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;
157
+ return Reflect.get(...arguments);
158
+ },
159
+ set: function(target, prop, value) {
160
+ target[prop] = value;
161
+ lastReflex = value;
162
+ return true;
163
+ }
164
+ });
165
+ const invokeLifecycleMethod = (reflex, stage) => {
166
+ const specificLifecycleMethod = reflex.controller[[ "before", "after", "finalize" ].includes(stage) ? `${stage}${camelize(reflex.action)}` : `${camelize(reflex.action, false)}${camelize(stage)}`];
167
+ const genericLifecycleMethod = reflex.controller[[ "before", "after", "finalize" ].includes(stage) ? `${stage}Reflex` : `reflex${camelize(stage)}`];
168
+ if (typeof specificLifecycleMethod === "function") {
169
+ specificLifecycleMethod.call(reflex.controller, reflex.element, reflex.target, reflex.error, reflex.id, reflex.payload);
170
+ }
171
+ if (typeof genericLifecycleMethod === "function") {
172
+ genericLifecycleMethod.call(reflex.controller, reflex.element, reflex.target, reflex.error, reflex.id, reflex.payload);
173
+ }
174
+ };
175
+ const dispatchLifecycleEvent = (reflex, stage) => {
176
+ if (!reflex.controller.element.parentElement) {
177
+ if (Debug$1.enabled && !reflex.warned) {
178
+ 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?`);
179
+ reflex.warned = true;
180
+ }
181
+ return;
182
+ }
183
+ reflex.stage = stage;
184
+ reflex.lifecycle.push(stage);
185
+ const event = `stimulus-reflex:${stage}`;
186
+ const action = `${event}:${reflex.action}`;
187
+ const detail = {
188
+ reflex: reflex.target,
189
+ controller: reflex.controller,
190
+ id: reflex.id,
191
+ element: reflex.element,
192
+ payload: reflex.payload
193
+ };
194
+ const options = {
195
+ bubbles: true,
196
+ cancelable: false,
197
+ detail: detail
198
+ };
199
+ reflex.controller.element.dispatchEvent(new CustomEvent(event, options));
200
+ reflex.controller.element.dispatchEvent(new CustomEvent(action, options));
201
+ if (window.jQuery) {
202
+ window.jQuery(reflex.controller.element).trigger(event, detail);
203
+ window.jQuery(reflex.controller.element).trigger(action, detail);
204
+ }
205
+ };
206
+ document.addEventListener("stimulus-reflex:before", (event => invokeLifecycleMethod(reflexes[event.detail.id], "before")), true);
207
+ document.addEventListener("stimulus-reflex:queued", (event => invokeLifecycleMethod(reflexes[event.detail.id], "queued")), true);
208
+ document.addEventListener("stimulus-reflex:delivered", (event => invokeLifecycleMethod(reflexes[event.detail.id], "delivered")), true);
209
+ document.addEventListener("stimulus-reflex:success", (event => {
210
+ const reflex = reflexes[event.detail.id];
211
+ invokeLifecycleMethod(reflex, "success");
212
+ dispatchLifecycleEvent(reflex, "after");
213
+ }), true);
214
+ document.addEventListener("stimulus-reflex:nothing", (event => dispatchLifecycleEvent(reflexes[event.detail.id], "success")), true);
215
+ document.addEventListener("stimulus-reflex:error", (event => {
216
+ const reflex = reflexes[event.detail.id];
217
+ invokeLifecycleMethod(reflex, "error");
218
+ dispatchLifecycleEvent(reflex, "after");
219
+ }), true);
220
+ document.addEventListener("stimulus-reflex:halted", (event => invokeLifecycleMethod(reflexes[event.detail.id], "halted")), true);
221
+ document.addEventListener("stimulus-reflex:forbidden", (event => invokeLifecycleMethod(reflexes[event.detail.id], "forbidden")), true);
222
+ document.addEventListener("stimulus-reflex:after", (event => invokeLifecycleMethod(reflexes[event.detail.id], "after")), true);
223
+ document.addEventListener("stimulus-reflex:finalize", (event => invokeLifecycleMethod(reflexes[event.detail.id], "finalize")), true);
224
+ let app = {};
225
+ var App = {
226
+ get app() {
227
+ return app;
228
+ },
229
+ set(application) {
230
+ app = application;
231
+ }
232
+ };
233
+ let isolationMode = false;
234
+ var IsolationMode = {
235
+ get disabled() {
236
+ return !isolationMode;
237
+ },
238
+ set(value) {
239
+ isolationMode = value;
240
+ if (Deprecate.enabled && !isolationMode) {
241
+ 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.")), {
242
+ once: true
243
+ });
244
+ }
245
+ }
246
+ };
247
+ class Reflex {
248
+ constructor(data, controller) {
249
+ this.data = data.valueOf();
250
+ this.controller = controller;
251
+ this.element = data.reflexElement;
252
+ this.id = data.id;
253
+ this.error = null;
254
+ this.payload = null;
255
+ this.stage = "created";
256
+ this.lifecycle = [ "created" ];
257
+ this.warned = false;
258
+ this.target = data.target;
259
+ this.action = data.target.split("#")[1];
260
+ this.selector = null;
261
+ this.morph = null;
262
+ this.operation = null;
263
+ this.timestamp = new Date;
264
+ this.cloned = false;
265
+ }
266
+ get getPromise() {
267
+ const promise = new Promise(((resolve, reject) => {
268
+ this.promise = {
269
+ resolve: resolve,
270
+ reject: reject,
271
+ data: this.data
272
+ };
273
+ }));
274
+ promise.id = this.id;
275
+ Object.defineProperty(promise, "reflexId", {
276
+ get() {
277
+ if (Deprecate.enabled) console.warn("reflexId is deprecated and will be removed from v4. Use id instead.");
278
+ return this.id;
279
+ }
280
+ });
281
+ promise.reflex = this;
282
+ if (Debug$1.enabled) promise.catch((() => {}));
283
+ return promise;
284
+ }
285
+ }
286
+ const received = data => {
287
+ if (!data.cableReady) return;
288
+ if (data.version.replace(".pre", "-pre") !== CableReady.version) {
289
+ 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}`);
290
+ return;
291
+ }
292
+ let reflexOperations = [];
293
+ for (let i = data.operations.length - 1; i >= 0; i--) {
294
+ if (data.operations[i].stimulusReflex) {
295
+ reflexOperations.push(data.operations[i]);
296
+ data.operations.splice(i, 1);
297
+ }
298
+ }
299
+ if (reflexOperations.some((operation => operation.stimulusReflex.url !== location.href))) {
300
+ if (Debug$1.enabled) {
301
+ console.error("Reflex failed due to mismatched URL.");
302
+ return;
303
+ }
304
+ }
305
+ let reflexData;
306
+ if (reflexOperations.length) {
307
+ reflexData = reflexOperations[0].stimulusReflex;
308
+ reflexData.payload = reflexOperations[0].payload;
309
+ }
310
+ if (reflexData) {
311
+ const {id: id, payload: payload} = reflexData;
312
+ let reflex;
313
+ if (!reflexes[id] && IsolationMode.disabled) {
314
+ const controllerElement = XPathToElement(reflexData.xpathController);
315
+ const reflexElement = XPathToElement(reflexData.xpathElement);
316
+ controllerElement.reflexController = controllerElement.reflexController || {};
317
+ controllerElement.reflexData = controllerElement.reflexData || {};
318
+ controllerElement.reflexError = controllerElement.reflexError || {};
319
+ const controller = App.app.getControllerForElementAndIdentifier(controllerElement, reflexData.reflexController);
320
+ controllerElement.reflexController[id] = controller;
321
+ controllerElement.reflexData[id] = reflexData;
322
+ reflex = new Reflex(reflexData, controller);
323
+ reflexes[id] = reflex;
324
+ reflex.cloned = true;
325
+ reflex.element = reflexElement;
326
+ controller.lastReflex = reflex;
327
+ dispatchLifecycleEvent(reflex, "before");
328
+ reflex.getPromise;
329
+ } else {
330
+ reflex = reflexes[id];
331
+ }
332
+ if (reflex) {
333
+ reflex.payload = payload;
334
+ reflex.totalOperations = reflexOperations.length;
335
+ reflex.pendingOperations = reflexOperations.length;
336
+ reflex.completedOperations = 0;
337
+ reflex.piggybackOperations = data.operations;
338
+ CableReady.perform(reflexOperations);
339
+ }
340
+ } else {
341
+ if (data.operations.length && reflexes[data.operations[0].reflexId]) {
342
+ CableReady.perform(data.operations);
343
+ }
344
+ }
345
+ };
346
+ let consumer;
347
+ let params;
348
+ let subscription;
349
+ let active;
350
+ const initialize$1 = (consumerValue, paramsValue) => {
351
+ consumer = consumerValue;
352
+ params = paramsValue;
353
+ document.addEventListener("DOMContentLoaded", (() => {
354
+ active = false;
355
+ connectionStatusClass();
356
+ 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.");
357
+ }));
358
+ document.addEventListener("turbolinks:load", connectionStatusClass);
359
+ document.addEventListener("turbo:load", connectionStatusClass);
360
+ };
361
+ const subscribe = controller => {
362
+ if (subscription) return;
363
+ consumer = consumer || controller.application.consumer || actioncable.createConsumer();
364
+ const {channel: channel} = controller.StimulusReflex;
365
+ const request = {
366
+ channel: channel,
367
+ ...params
368
+ };
369
+ const identifier = JSON.stringify(request);
370
+ subscription = consumer.subscriptions.findAll(identifier)[0] || consumer.subscriptions.create(request, {
371
+ received: received,
372
+ connected: connected,
373
+ rejected: rejected,
374
+ disconnected: disconnected
375
+ });
376
+ };
377
+ const connected = () => {
378
+ active = true;
379
+ connectionStatusClass();
380
+ emitEvent("stimulus-reflex:connected");
381
+ Object.values(reflexes.queued).forEach((reflex => {
382
+ subscription.send(reflex.data);
383
+ dispatchLifecycleEvent(reflex, "delivered");
384
+ }));
385
+ };
386
+ const rejected = () => {
387
+ active = false;
388
+ connectionStatusClass();
389
+ emitEvent("stimulus-reflex:rejected");
390
+ if (Debug.enabled) console.warn("Channel subscription was rejected.");
391
+ };
392
+ const disconnected = willAttemptReconnect => {
393
+ active = false;
394
+ connectionStatusClass();
395
+ emitEvent("stimulus-reflex:disconnected", willAttemptReconnect);
396
+ };
397
+ const deliver = reflex => {
398
+ if (active) {
399
+ subscription.send(reflex.data);
400
+ dispatchLifecycleEvent(reflex, "delivered");
401
+ } else dispatchLifecycleEvent(reflex, "queued");
402
+ };
403
+ const connectionStatusClass = () => {
404
+ const list = document.body.classList;
405
+ if (!(list.contains("stimulus-reflex-connected") || list.contains("stimulus-reflex-disconnected"))) {
406
+ list.add(active ? "stimulus-reflex-connected" : "stimulus-reflex-disconnected");
407
+ return;
408
+ }
409
+ if (active) {
410
+ list.replace("stimulus-reflex-disconnected", "stimulus-reflex-connected");
411
+ } else {
412
+ list.replace("stimulus-reflex-connected", "stimulus-reflex-disconnected");
190
413
  }
191
- return reverse ? snapshots.reverse() : snapshots;
414
+ };
415
+ var ActionCableTransport = {
416
+ subscribe: subscribe,
417
+ deliver: deliver,
418
+ initialize: initialize$1
419
+ };
420
+ const request = reflex => {
421
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
422
+ console.log(`↑ stimulus ↑ ${reflex.target}`, {
423
+ id: reflex.id,
424
+ args: reflex.data.args,
425
+ controller: reflex.controller.identifier,
426
+ element: reflex.element,
427
+ controllerElement: reflex.controller.element
428
+ });
429
+ };
430
+ const success = reflex => {
431
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
432
+ const output = {
433
+ id: reflex.id,
434
+ morph: reflex.morph,
435
+ payload: reflex.payload
436
+ };
437
+ if (reflex.operation !== "dispatch_event") output.operation = reflex.operation;
438
+ console.log(`↓ reflex ↓ ${reflex.target} → ${reflex.selector || "∞"}${progress(reflex)} ${duration(reflex)}`, output);
439
+ };
440
+ const halted$1 = reflex => {
441
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
442
+ console.log(`↓ reflex ↓ ${reflex.target} ${duration(reflex)} %cHALTED`, "color: #ffa500;", {
443
+ id: reflex.id,
444
+ payload: reflex.payload
445
+ });
446
+ };
447
+ const forbidden$1 = reflex => {
448
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
449
+ console.log(`↓ reflex ↓ ${reflex.target} ${duration(reflex)} %cFORBIDDEN`, "color: #BF40BF;", {
450
+ id: reflex.id,
451
+ payload: reflex.payload
452
+ });
453
+ };
454
+ const error$1 = reflex => {
455
+ if (Debug$1.disabled || reflex.data.suppressLogging) return;
456
+ console.log(`↓ reflex ↓ ${reflex.target} ${duration(reflex)} %cERROR: ${reflex.error}`, "color: #f00;", {
457
+ id: reflex.id,
458
+ payload: reflex.payload
459
+ });
460
+ };
461
+ const duration = reflex => !reflex.cloned ? `in ${new Date - reflex.timestamp}ms` : "CLONED";
462
+ const progress = reflex => reflex.totalOperations > 1 ? ` ${reflex.completedOperations}/${reflex.totalOperations}` : "";
463
+ var Log = {
464
+ request: request,
465
+ success: success,
466
+ halted: halted$1,
467
+ forbidden: forbidden$1,
468
+ error: error$1
192
469
  };
193
470
  const multipleInstances = element => {
194
471
  if ([ "checkbox", "radio" ].includes(element.type)) {
@@ -198,8 +475,8 @@
198
475
  };
199
476
  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
477
  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;
478
+ const value = Array.from(new Set(values.filter((v => v && String(v).length)).map((v => v.trim())))).join(" ").trim();
479
+ return value.length > 0 ? value : null;
203
480
  };
204
481
  const attributeValues = value => {
205
482
  if (!value) return [];
@@ -307,217 +584,76 @@
307
584
  }
308
585
  return attrs;
309
586
  };
310
- let isolationMode = false;
311
- var IsolationMode = {
312
- get disabled() {
313
- return !isolationMode;
314
- },
315
- set(value) {
316
- isolationMode = value;
317
- }
587
+ var name = "stimulus_reflex";
588
+ var version = "3.5.0-pre10";
589
+ var description = "Build reactive applications with the Rails tooling you already know and love.";
590
+ var keywords = [ "ruby", "rails", "websockets", "actioncable", "turbolinks", "reactive", "cable", "ujs", "ssr", "stimulus", "reflex", "stimulus_reflex", "dom", "morphdom" ];
591
+ var homepage = "https://docs.stimulusreflex.com";
592
+ var bugs = "https://github.com/stimulusreflex/stimulus_reflex/issues";
593
+ var repository = "https://github.com/stimulusreflex/stimulus_reflex";
594
+ var license = "MIT";
595
+ var author = "Nathan Hopkins <natehop@gmail.com>";
596
+ var contributors = [ "Andrew Mason <andrewmcodes@protonmail.com>", "Julian Rubisch <julian@julianrubisch.at>", "Marco Roth <marco.roth@intergga.ch>", "Nathan Hopkins <natehop@gmail.com>" ];
597
+ var main = "./dist/stimulus_reflex.js";
598
+ var module = "./dist/stimulus_reflex.js";
599
+ var browser = "./dist/stimulus_reflex.js";
600
+ var unpkg = "./dist/stimulus_reflex.umd.js";
601
+ var umd = "./dist/stimulus_reflex.umd.js";
602
+ var files = [ "dist/*", "javascript/*" ];
603
+ var scripts = {
604
+ lint: "yarn run format --check",
605
+ format: "yarn run prettier-standard ./javascript/**/*.js rollup.config.mjs",
606
+ build: "yarn rollup -c",
607
+ "build:watch": "yarn rollup -wc",
608
+ watch: "yarn build:watch",
609
+ test: "web-test-runner javascript/test/**/*.test.js",
610
+ "docs:dev": "vitepress dev docs",
611
+ "docs:build": "vitepress build docs",
612
+ "docs:preview": "vitepress preview docs"
318
613
  };
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
- }
614
+ var peerDependencies = {
615
+ "@hotwired/stimulus": ">= 3.0"
339
616
  };
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
- }
617
+ var dependencies = {
618
+ "@hotwired/stimulus": ">= 3.0, < 4",
619
+ "@rails/actioncable": ">= 6.0, < 8",
620
+ cable_ready: "5.0.0-pre10"
392
621
  };
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;
622
+ var devDependencies = {
623
+ "@open-wc/testing": "^3.1.7",
624
+ "@rollup/plugin-json": "^6.0.0",
625
+ "@rollup/plugin-node-resolve": "^15.0.1",
626
+ "@rollup/plugin-terser": "^0.4.0",
627
+ "@web/dev-server-esbuild": "^0.3.3",
628
+ "@web/dev-server-rollup": "^0.3.21",
629
+ "@web/test-runner": "^0.15.0",
630
+ "prettier-standard": "^16.4.1",
631
+ rollup: "^3.17.1",
632
+ vitepress: "^1.0.0-alpha.47"
405
633
  };
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];
634
+ var packageInfo = {
635
+ name: name,
636
+ version: version,
637
+ description: description,
638
+ keywords: keywords,
639
+ homepage: homepage,
640
+ bugs: bugs,
641
+ repository: repository,
642
+ license: license,
643
+ author: author,
644
+ contributors: contributors,
645
+ main: main,
646
+ module: module,
647
+ browser: browser,
648
+ import: "./dist/stimulus_reflex.js",
649
+ unpkg: unpkg,
650
+ umd: umd,
651
+ files: files,
652
+ scripts: scripts,
653
+ peerDependencies: peerDependencies,
654
+ dependencies: dependencies,
655
+ devDependencies: devDependencies
412
656
  };
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
657
  class ReflexData {
522
658
  constructor(options, reflexElement, controllerElement, reflexController, permanentAttributeName, target, args, url, tabId) {
523
659
  this.options = options;
@@ -534,9 +670,9 @@
534
670
  this._attrs = this._attrs || this.options["attrs"] || extractElementAttributes(this.reflexElement);
535
671
  return this._attrs;
536
672
  }
537
- get reflexId() {
538
- this._reflexId = this._reflexId || this.options["reflexId"] || uuidv4();
539
- return this._reflexId;
673
+ get id() {
674
+ this._id = this._id || this.options["id"] || uuidv4();
675
+ return this._id;
540
676
  }
541
677
  get selectors() {
542
678
  this._selectors = this._selectors || this.options["selectors"] || getReflexRoots(this.reflexElement);
@@ -581,7 +717,7 @@
581
717
  attrs: this.attrs,
582
718
  dataset: this.dataset,
583
719
  selectors: this.selectors,
584
- reflexId: this.reflexId,
720
+ id: this.id,
585
721
  resolveLate: this.resolveLate,
586
722
  suppressLogging: this.suppressLogging,
587
723
  xpathController: this.xpathController,
@@ -595,198 +731,202 @@
595
731
  args: this.args,
596
732
  url: this.url,
597
733
  tabId: this.tabId,
598
- version: version
734
+ version: packageInfo.version
599
735
  };
600
736
  }
601
737
  }
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;
738
+ let transport = {};
739
+ var Transport = {
740
+ get plugin() {
741
+ return transport;
644
742
  },
645
- createSubscription: createSubscription,
646
- connected: connected,
647
- rejected: rejected,
648
- disconnected: disconnected,
649
- set(consumerValue, paramsValue) {
650
- consumer = consumerValue;
651
- params = paramsValue;
743
+ set(newTransport) {
744
+ transport = newTransport;
652
745
  }
653
746
  };
654
747
  const beforeDOMUpdate = event => {
655
- const {stimulusReflex: stimulusReflex, payload: payload} = event.detail || {};
748
+ const {stimulusReflex: stimulusReflex} = event.detail || {};
656
749
  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;
750
+ const reflex = reflexes[stimulusReflex.id];
662
751
  reflex.pendingOperations--;
663
752
  if (reflex.pendingOperations > 0) return;
664
- if (!stimulusReflex.resolveLate) setTimeout((() => promise.resolve({
665
- element: reflexElement,
753
+ if (!stimulusReflex.resolveLate) setTimeout((() => reflex.promise.resolve({
754
+ element: reflex.element,
666
755
  event: event,
667
- data: promise.data,
668
- payload: payload,
669
- reflexId: reflexId,
756
+ data: reflex.data,
757
+ payload: reflex.payload,
758
+ id: reflex.id,
670
759
  toString: () => ""
671
760
  })));
672
- setTimeout((() => dispatchLifecycleEvent("success", reflexElement, controllerElement, reflexId, payload)));
761
+ setTimeout((() => dispatchLifecycleEvent(reflex, "success")));
673
762
  };
674
763
  const afterDOMUpdate = event => {
675
- const {stimulusReflex: stimulusReflex, payload: payload} = event.detail || {};
764
+ const {stimulusReflex: stimulusReflex} = event.detail || {};
676
765
  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;
766
+ const reflex = reflexes[stimulusReflex.id];
682
767
  reflex.completedOperations++;
683
- Log.success(event, false);
768
+ reflex.selector = event.detail.selector;
769
+ reflex.morph = event.detail.stimulusReflex.morph;
770
+ reflex.operation = event.type.split(":")[1].split("-").slice(1).join("_");
771
+ Log.success(reflex);
684
772
  if (reflex.completedOperations < reflex.totalOperations) return;
685
- if (stimulusReflex.resolveLate) setTimeout((() => promise.resolve({
686
- element: reflexElement,
773
+ if (stimulusReflex.resolveLate) setTimeout((() => reflex.promise.resolve({
774
+ element: reflex.element,
687
775
  event: event,
688
- data: promise.data,
689
- payload: payload,
690
- reflexId: reflexId,
776
+ data: reflex.data,
777
+ payload: reflex.payload,
778
+ id: reflex.id,
691
779
  toString: () => ""
692
780
  })));
693
- setTimeout((() => dispatchLifecycleEvent("finalize", reflexElement, controllerElement, reflexId, payload)));
694
- if (reflex.piggybackOperations.length) CableReady__default["default"].perform(reflex.piggybackOperations);
781
+ setTimeout((() => dispatchLifecycleEvent(reflex, "finalize")));
782
+ if (reflex.piggybackOperations.length) CableReady.perform(reflex.piggybackOperations);
695
783
  };
696
784
  const routeReflexEvent = event => {
697
- const {stimulusReflex: stimulusReflex, payload: payload, name: name, body: body} = event.detail || {};
785
+ const {stimulusReflex: stimulusReflex, name: name} = event.detail || {};
698
786
  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,
787
+ const eventTypes = {
788
+ nothing: nothing,
789
+ halted: halted,
790
+ forbidden: forbidden,
791
+ error: error
792
+ };
793
+ if (!stimulusReflex || !Object.keys(eventTypes).includes(eventType)) return;
794
+ const reflex = reflexes[stimulusReflex.id];
795
+ reflex.completedOperations++;
796
+ reflex.pendingOperations--;
797
+ reflex.selector = event.detail.selector;
798
+ reflex.morph = event.detail.stimulusReflex.morph;
799
+ reflex.operation = event.type.split(":")[1].split("-").slice(1).join("_");
800
+ if (eventType === "error") reflex.error = event.detail.error;
801
+ eventTypes[eventType](reflex, event);
802
+ setTimeout((() => dispatchLifecycleEvent(reflex, eventType)));
803
+ if (reflex.piggybackOperations.length) CableReady.perform(reflex.piggybackOperations);
804
+ };
805
+ const nothing = (reflex, event) => {
806
+ Log.success(reflex);
807
+ setTimeout((() => reflex.promise.resolve({
808
+ data: reflex.data,
809
+ element: reflex.element,
810
+ event: event,
811
+ payload: reflex.payload,
812
+ id: reflex.id,
813
+ toString: () => ""
814
+ })));
815
+ };
816
+ const halted = (reflex, event) => {
817
+ Log.halted(reflex, event);
818
+ setTimeout((() => reflex.promise.resolve({
819
+ data: reflex.data,
820
+ element: reflex.element,
731
821
  event: event,
732
- payload: payload,
733
- reflexId: promise.data.reflexId,
822
+ payload: reflex.payload,
823
+ id: reflex.id,
734
824
  toString: () => ""
735
825
  })));
736
826
  };
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,
827
+ const forbidden = (reflex, event) => {
828
+ Log.forbidden(reflex, event);
829
+ setTimeout((() => reflex.promise.resolve({
830
+ data: reflex.data,
831
+ element: reflex.element,
743
832
  event: event,
744
- payload: payload,
745
- reflexId: promise.data.reflexId,
833
+ payload: reflex.payload,
834
+ id: reflex.id,
746
835
  toString: () => ""
747
836
  })));
748
837
  };
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,
838
+ const error = (reflex, event) => {
839
+ Log.error(reflex, event);
840
+ setTimeout((() => reflex.promise.reject({
841
+ data: reflex.data,
842
+ element: reflex.element,
755
843
  event: event,
756
- payload: payload,
757
- reflexId: promise.data.reflexId,
758
- error: event.detail.body,
759
- toString: () => event.detail.body
844
+ payload: reflex.payload,
845
+ id: reflex.id,
846
+ error: reflex.error,
847
+ toString: () => reflex.error
760
848
  })));
761
849
  };
850
+ const localReflexControllers = element => attributeValues(element.getAttribute(Schema.controller)).reduce(((memo, name) => {
851
+ const controller = App.app.getControllerForElementAndIdentifier(element, name);
852
+ if (controller && controller.StimulusReflex) memo.push(controller);
853
+ return memo;
854
+ }), []);
855
+ const allReflexControllers = element => {
856
+ let controllers = [];
857
+ while (element) {
858
+ controllers = controllers.concat(localReflexControllers(element));
859
+ element = element.parentElement;
860
+ }
861
+ return controllers;
862
+ };
863
+ const findControllerByReflexName = (reflexName, controllers) => {
864
+ const controller = controllers.find((controller => {
865
+ if (!controller.identifier) return;
866
+ return extractReflexName(reflexName).replace(/([a-z0–9])([A-Z])/g, "$1-$2").replace(/(::)/g, "--").toLowerCase() === controller.identifier;
867
+ }));
868
+ return controller || controllers[0];
869
+ };
870
+ const scanForReflexes = debounce((() => {
871
+ const reflexElements = document.querySelectorAll(`[${Schema.reflex}]`);
872
+ reflexElements.forEach((element => scanForReflexesOnElement(element)));
873
+ }), 20);
874
+ const scanForReflexesOnElement = element => {
875
+ const controllerAttribute = element.getAttribute(Schema.controller);
876
+ const controllers = attributeValues(controllerAttribute);
877
+ const reflexAttribute = element.getAttribute(Schema.reflex);
878
+ const reflexAttributeNames = attributeValues(reflexAttribute);
879
+ const actionAttribute = element.getAttribute(Schema.action);
880
+ const actions = attributeValues(actionAttribute).filter((action => !action.includes("#__perform")));
881
+ reflexAttributeNames.forEach((reflexName => {
882
+ const controller = findControllerByReflexName(reflexName, allReflexControllers(element));
883
+ const controllerName = controller ? controller.identifier : "stimulus-reflex";
884
+ actions.push(`${reflexName.split("->")[0]}->${controllerName}#__perform`);
885
+ controllers.push(controllerName);
886
+ }));
887
+ const controllerValue = attributeValue(controllers);
888
+ const actionValue = attributeValue(actions);
889
+ let emitReadyEvent = false;
890
+ if (controllerValue && element.getAttribute(Schema.controller) != controllerValue) {
891
+ element.setAttribute(Schema.controller, controllerValue);
892
+ emitReadyEvent = true;
893
+ }
894
+ if (actionValue && element.getAttribute(Schema.action) != actionValue) {
895
+ element.setAttribute(Schema.action, actionValue);
896
+ emitReadyEvent = true;
897
+ }
898
+ if (emitReadyEvent) {
899
+ dispatch(element, "stimulus-reflex:ready", {
900
+ reflex: reflexAttribute,
901
+ controller: controllerValue,
902
+ action: actionValue,
903
+ element: element
904
+ });
905
+ }
906
+ };
762
907
  class StimulusReflexController extends stimulus.Controller {
763
908
  constructor(...args) {
764
909
  super(...args);
765
910
  register(this);
766
911
  }
767
912
  }
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
- });
913
+ const tabId = uuidv4();
914
+ const initialize = (application, {controller: controller, consumer: consumer, debug: debug, params: params, isolate: isolate, deprecate: deprecate, transport: transport} = {}) => {
915
+ Transport.set(transport || ActionCableTransport);
916
+ Transport.plugin.initialize(consumer, params);
778
917
  IsolationMode.set(!!isolate);
779
- reflexes.app = application;
918
+ App.set(application);
780
919
  Schema.set(application);
781
- reflexes.app.register("stimulus-reflex", controller || StimulusReflexController);
920
+ App.app.register("stimulus-reflex", controller || StimulusReflexController);
782
921
  Debug$1.set(!!debug);
783
922
  if (typeof deprecate !== "undefined") Deprecate.set(deprecate);
784
- const observer = new MutationObserver(setupDeclarativeReflexes);
923
+ const observer = new MutationObserver(scanForReflexes);
785
924
  observer.observe(document.documentElement, {
786
925
  attributeFilter: [ Schema.reflex, Schema.action ],
787
926
  childList: true,
788
927
  subtree: true
789
928
  });
929
+ emitEvent("stimulus-reflex:initialized");
790
930
  };
791
931
  const register = (controller, options = {}) => {
792
932
  const channel = "StimulusReflex::Channel";
@@ -794,38 +934,32 @@
794
934
  ...options,
795
935
  channel: channel
796
936
  };
797
- ActionCableTransport.createSubscription(controller);
937
+ Transport.plugin.subscribe(controller);
798
938
  Object.assign(controller, {
799
- isActionCableConnectionOpen() {
800
- return this.StimulusReflex.subscription.consumer.connection.isOpen();
801
- },
802
939
  stimulate() {
803
940
  const url = location.href;
941
+ const controllerElement = this.element;
804
942
  const args = Array.from(arguments);
805
943
  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) {
944
+ const reflexElement = getReflexElement(args, controllerElement);
945
+ if (elementInvalid(reflexElement)) {
809
946
  if (Debug$1.enabled) console.warn("Reflex aborted: invalid numeric input");
810
947
  return;
811
948
  }
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
- }
949
+ const options = getReflexOptions(args);
817
950
  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.";
951
+ const id = reflexData.id;
821
952
  controllerElement.reflexController = controllerElement.reflexController || {};
822
953
  controllerElement.reflexData = controllerElement.reflexData || {};
823
954
  controllerElement.reflexError = controllerElement.reflexError || {};
824
- controllerElement.reflexController[reflexId] = this;
825
- controllerElement.reflexData[reflexId] = reflexData.valueOf();
826
- dispatchLifecycleEvent("before", reflexElement, controllerElement, reflexId);
955
+ controllerElement.reflexController[id] = this;
956
+ controllerElement.reflexData[id] = reflexData.valueOf();
957
+ const reflex = new Reflex(reflexData, this);
958
+ reflexes[id] = reflex;
959
+ this.lastReflex = reflex;
960
+ dispatchLifecycleEvent(reflex, "before");
827
961
  setTimeout((() => {
828
- const {params: params} = controllerElement.reflexData[reflexId] || {};
962
+ const {params: params} = controllerElement.reflexData[id] || {};
829
963
  const check = reflexElement.attributes[Schema.reflexSerializeForm];
830
964
  if (check) {
831
965
  options["serializeForm"] = check.value !== "false";
@@ -835,16 +969,16 @@
835
969
  const formData = options["serializeForm"] === false ? "" : serializeForm(form, {
836
970
  element: reflexElement
837
971
  });
838
- controllerElement.reflexData[reflexId] = {
972
+ reflex.data = {
839
973
  ...reflexData.valueOf(),
840
974
  params: params,
841
975
  formData: formData
842
976
  };
843
- this.StimulusReflex.subscription.send(controllerElement.reflexData[reflexId]);
977
+ controllerElement.reflexData[id] = reflex.data;
978
+ Transport.plugin.deliver(reflex);
844
979
  }));
845
- const promise = registerReflex(reflexData.valueOf());
846
- Log.request(reflexId, target, args, this.context.scope.identifier, reflexElement, controllerElement);
847
- return promise;
980
+ Log.request(reflex);
981
+ return reflex.getPromise;
848
982
  },
849
983
  __perform(event) {
850
984
  let element = event.target;
@@ -861,8 +995,23 @@
861
995
  }
862
996
  }
863
997
  });
998
+ if (!controller.reflexes) Object.defineProperty(controller, "reflexes", {
999
+ get() {
1000
+ return new Proxy(reflexes, {
1001
+ get: function(target, prop) {
1002
+ if (prop === "last") return this.lastReflex;
1003
+ return Object.fromEntries(Object.entries(target[prop]).filter((([_, reflex]) => reflex.controller === this)));
1004
+ }.bind(this)
1005
+ });
1006
+ }
1007
+ });
1008
+ scanForReflexesOnElement(controller.element);
1009
+ emitEvent("stimulus-reflex:controller-registered", {
1010
+ detail: {
1011
+ controller: controller
1012
+ }
1013
+ });
864
1014
  };
865
- const tabId = uuidv4();
866
1015
  const useReflex = (controller, options = {}) => {
867
1016
  register(controller, options);
868
1017
  };
@@ -871,14 +1020,22 @@
871
1020
  document.addEventListener("cable-ready:before-morph", beforeDOMUpdate);
872
1021
  document.addEventListener("cable-ready:after-inner-html", afterDOMUpdate);
873
1022
  document.addEventListener("cable-ready:after-morph", afterDOMUpdate);
874
- window.addEventListener("load", setupDeclarativeReflexes);
1023
+ document.addEventListener("readystatechange", (() => {
1024
+ if (document.readyState === "complete") {
1025
+ scanForReflexes();
1026
+ }
1027
+ }));
875
1028
  var StimulusReflex = Object.freeze({
876
1029
  __proto__: null,
877
1030
  initialize: initialize,
1031
+ reflexes: reflexes,
878
1032
  register: register,
1033
+ scanForReflexes: scanForReflexes,
1034
+ scanForReflexesOnElement: scanForReflexesOnElement,
879
1035
  useReflex: useReflex
880
1036
  });
881
1037
  const global = {
1038
+ version: packageInfo.version,
882
1039
  ...StimulusReflex,
883
1040
  get debug() {
884
1041
  return Debug$1.value;
@@ -894,9 +1051,12 @@
894
1051
  }
895
1052
  };
896
1053
  window.StimulusReflex = global;
897
- exports["default"] = global;
1054
+ exports.default = global;
898
1055
  exports.initialize = initialize;
1056
+ exports.reflexes = reflexes;
899
1057
  exports.register = register;
1058
+ exports.scanForReflexes = scanForReflexes;
1059
+ exports.scanForReflexesOnElement = scanForReflexesOnElement;
900
1060
  exports.useReflex = useReflex;
901
1061
  Object.defineProperty(exports, "__esModule", {
902
1062
  value: true