studio-engine 0.93.0 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,306 @@
1
+ // studio/pinned_stack: THE PINNED STACK. Publishes the live geometry of every layer
2
+ // of pinned page chrome as CSS custom properties, so anything fixed or sticky
3
+ // positions off it in CSS with no hardcoded px and no JS of its own.
4
+ //
5
+ // A layer joins by carrying `data-pin="<name>"`. Nothing else is required:
6
+ // no registration call, no declared stacking order, no consumer edit.
7
+ //
8
+ // WHAT A CONSUMER READS — prefer the first two; they are the reason this is a
9
+ // primitive rather than an arithmetic helper:
10
+ //
11
+ // --pin-stack-bottom the bottom of the WHOLE stack. What something sitting
12
+ // BENEATH all the pinned chrome positions off.
13
+ // --pin-<name>-top the bottom of everything ABOVE that layer. What a layer
14
+ // that is ITSELF in the stack positions off.
15
+ // --pin-<name>-h that layer's HEIGHT.
16
+ // --pin-<name>-bottom that layer's own BOTTOM EDGE, in viewport coordinates.
17
+ // --nav-h / --nav-bottom the <header>'s height and bottom edge, under their
18
+ // legacy names, from the same measurement.
19
+ //
20
+ // DO NOT COMPOSE THE STACK IN A CONSUMER. `top: max(var(--pin-nav-bottom),
21
+ // var(--pin-apps-bottom))` is what this replaced, and it fails twice: it does
22
+ // not scale, because a fourth layer means editing every consumer that ever
23
+ // wanted to sit under the stack; and it is not sound, because a max() over two
24
+ // custom properties is only meaningful if they were written in the same frame.
25
+ // --pin-stack-bottom is one value that cannot disagree with itself.
26
+ //
27
+ // --nav-h AND --nav-bottom ARE NOT THE SAME NUMBER. They are equal only when
28
+ // the header sits at the top of the viewport, which is why one was long
29
+ // mistaken for the other. An app that stacks chrome ABOVE the header — an
30
+ // environment banner rendered before the navbar, as mcritchie-industries does —
31
+ // pushes the header down, and a `fixed` overlay positioned at --nav-h then
32
+ // rides UP over the header by exactly the height of that chrome. Use --nav-h to
33
+ // SIZE something as tall as the header; use --nav-bottom to START something
34
+ // underneath it. Same distinction for --pin-<name>-h vs --pin-<name>-bottom.
35
+ //
36
+ // Started once per document by studio/application. The listeners it adds live
37
+ // for the document, and a Turbo visit republishes through them rather than
38
+ // starting it again.
39
+
40
+ // THE STACK, COMPOSED HERE RATHER THAN BY EVERY CONSUMER.
41
+ //
42
+ // Consumers used to write `top: max(var(--pin-nav-bottom), var(--pin-apps-bottom))`
43
+ // — every layer enumerated, in every consumer. That is two failures at once.
44
+ // It does not SCALE: a fourth pinned layer means editing every consumer that
45
+ // ever wanted to sit under the stack. And it is not SOUND: max() over two
46
+ // independently-written custom properties is only meaningful if they were
47
+ // written in the same frame, and any skew between them is expressed instantly
48
+ // as layout. Composing here means a consumer reads ONE value that cannot
49
+ // disagree with itself.
50
+ //
51
+ // --pin-stack-bottom the bottom of the WHOLE pinned stack. What something
52
+ // sitting beneath all the chrome positions off.
53
+ // --pin-<name>-top the bottom of everything ABOVE that layer. What a
54
+ // layer that is ITSELF in the stack positions off —
55
+ // --pin-stack-bottom would include the layer's own
56
+ // edge, so a layer positioned off it would chase itself
57
+ // down the page.
58
+ //
59
+ // ORDER is document order, which is the order the layers were declared and
60
+ // therefore the order they read in. `data-pin-order` overrides it for a layer
61
+ // whose DOM position does not match where it sits on screen; it is a plain
62
+ // number, lower is higher up, and it needs to exist on only the layer that
63
+ // disagrees rather than on all of them.
64
+ export function order(p) {
65
+ var raw = parseFloat(p.el.getAttribute('data-pin-order'));
66
+ return isNaN(raw) ? null : raw;
67
+ }
68
+ export function composed(pins) {
69
+ var i, j, stack = 0;
70
+ for (i = 0; i < pins.length; i++) {
71
+ if (pins[i].bottom > stack) stack = pins[i].bottom;
72
+ var above = 0;
73
+ for (j = 0; j < pins.length; j++) {
74
+ if (i === j) continue;
75
+ var oi = order(pins[i]), oj = order(pins[j]);
76
+ // Both ordered: compare the numbers. Otherwise fall back to document
77
+ // order, which querySelectorAll already returns them in.
78
+ var jIsAbove = (oi !== null && oj !== null) ? oj < oi : j < i;
79
+ if (jIsAbove && pins[j].bottom > above) above = pins[j].bottom;
80
+ }
81
+ pins[i].top = above;
82
+ }
83
+ return stack;
84
+ }
85
+
86
+ var started = false
87
+
88
+ export function startPinnedStack() {
89
+ if (started || !window.ResizeObserver) return;
90
+ started = true;
91
+ var ro = null;
92
+ var queued = false;
93
+ // WHAT WAS LAST PUBLISHED, per layer name: { h, bottom, top }. It does two
94
+ // jobs. It is the record of which names this publisher has written, so a
95
+ // DEPARTED layer can be cleared. And it is what lets an UNCHANGED value skip
96
+ // its write — these are INHERITED custom properties on documentElement, so
97
+ // even a write of the same number invalidates style for the whole document,
98
+ // and a scroll frame touches this four times per layer.
99
+ var last = {};
100
+ var lastStack = null;
101
+ // SKIPPING AN UNCHANGED WRITE IS ONLY SAFE WHILE NOTHING ELSE TOUCHES THESE
102
+ // PROPERTIES. On a scroll frame that holds, and skipping is most of why this
103
+ // is cheap. On a STRUCTURAL change it does not: a layer arriving or leaving,
104
+ // a Turbo patch, a fresh document — anything there may have cleared a
105
+ // property out from under the cache, and a skip would then never write it
106
+ // again. So a structural change forces a full write, once.
107
+ var forceWrite = true;
108
+ // The nodes currently under observation, held only to know what to unobserve
109
+ // when one departs. The REGISTRY ITSELF is never cached; see readPins().
110
+ var observed = [];
111
+
112
+ // THE REGISTRY IS DERIVED, NEVER CACHED — and that is a correctness rule.
113
+ //
114
+ // This used to build a `pins` array once and hold each element by reference
115
+ // until something re-registered it. A Turbo Stream replaces a pinned layer's
116
+ // node WITHOUT a turbo:load, so between the patch and the re-scan the held
117
+ // reference pointed at the DETACHED predecessor — and a detached node's rect
118
+ // is all zeros, which is indistinguishable from a layer that is legitimately
119
+ // hidden. Measured on mcritchie-studio's /deployments: --pin-apps-bottom
120
+ // published 0px while the live strip stood at display:block, bottom 152px,
121
+ // and the board's lane headers slammed 99px up and back on every broadcast.
122
+ //
123
+ // Re-querying costs one querySelectorAll over a handful of nodes per publish.
124
+ // That is cheaper than the class of bug it removes, because it makes a stale
125
+ // reference IMPOSSIBLE rather than merely unlikely: there is no window between
126
+ // a DOM patch and a re-scan in which this can be pointed at the wrong node.
127
+ function readPins() {
128
+ var header = document.querySelector('header');
129
+ var nodes = document.querySelectorAll('[data-pin]');
130
+ var list = [];
131
+ var headerPinned = false;
132
+ for (var i = 0; i < nodes.length; i++) {
133
+ var name = nodes[i].getAttribute('data-pin');
134
+ // An unnamed pin would write `--pin--h`, a valid property name and a
135
+ // silent nonsense one. Skip it rather than publish it.
136
+ if (!name) continue;
137
+ if (nodes[i] === header) headerPinned = true;
138
+ list.push(measure(nodes[i], name, nodes[i] === header));
139
+ }
140
+ // THE HOST-OWNED HEADER, and the back-compat promise that depends on it.
141
+ // A header that does not carry [data-pin] is not in the registry, so the
142
+ // legacy --nav-h / --nav-bottom — which studio/_sidebar_panel, turf's
143
+ // contest board and mcritchie-studio's heartbeat CSS all read — would
144
+ // never publish at all. Both live consumers own their header, so it is
145
+ // adopted as a pin even when it does not ask to be.
146
+ if (header && !headerPinned) list.push(measure(header, 'nav', true));
147
+ return list;
148
+ }
149
+
150
+ // ONE READ PER ELEMENT PER PUBLISH, and every read before every write.
151
+ // A read after a write to the root forces a fresh layout, which is the
152
+ // thrash this publisher exists without. Measured by review at 6x CPU
153
+ // throttle over 175 frames: frames past 20ms 62 vs 42, median 17.7 vs
154
+ // 14.9ms, p90 34.2 vs 26.0ms; at 10x, 84/180 vs 4/180.
155
+ function measure(el, name, legacy) {
156
+ // A hidden layer needs no special case: display:none gives offsetHeight 0
157
+ // and an all-zero rect, so it measures 0 and drops out of the stack on its
158
+ // own. That is what lets a layer come and go without anyone declaring a
159
+ // stacking order, and it is load-bearing rather than incidental.
160
+ var r = el.getBoundingClientRect();
161
+ return { el: el, name: name, legacy: legacy, h: el.offsetHeight, bottom: Math.max(0, r.bottom) };
162
+ }
163
+
164
+
165
+ function write(pins, stack) {
166
+ var style = document.documentElement.style;
167
+ var seen = {};
168
+ var i, p, prev;
169
+ // A ROSTER CHANGE IS STRUCTURAL TOO, and it is not always announced by an
170
+ // event: an x-show that adds the node, a layer rendered by something other
171
+ // than a stream. Compare the names against what was published last.
172
+ var force = forceWrite;
173
+ forceWrite = false;
174
+ if (!force) {
175
+ for (i = 0; i < pins.length; i++) { if (!last.hasOwnProperty(pins[i].name)) { force = true; break; } }
176
+ }
177
+ for (i = 0; i < pins.length; i++) {
178
+ p = pins[i];
179
+ seen[p.name] = true;
180
+ prev = last[p.name] || {};
181
+ if (force || p.h !== prev.h) {
182
+ style.setProperty('--pin-' + p.name + '-h', p.h + 'px');
183
+ if (p.legacy) style.setProperty('--nav-h', p.h + 'px');
184
+ }
185
+ if (force || p.bottom !== prev.bottom) {
186
+ style.setProperty('--pin-' + p.name + '-bottom', p.bottom + 'px');
187
+ if (p.legacy) style.setProperty('--nav-bottom', p.bottom + 'px');
188
+ }
189
+ if (force || p.top !== prev.top) style.setProperty('--pin-' + p.name + '-top', p.top + 'px');
190
+ last[p.name] = { h: p.h, bottom: p.bottom, top: p.top };
191
+ }
192
+ if (force || stack !== lastStack) {
193
+ style.setProperty('--pin-stack-bottom', stack + 'px');
194
+ lastStack = stack;
195
+ }
196
+
197
+ // CLEAR WHAT LEFT. A layer goes away three ways: display:none and x-show
198
+ // both keep the node, so it measures 0 and drops out of the stack on its
199
+ // own. REMOVAL does not — nothing is left to measure, and without this the
200
+ // last published value stands forever while the consumer sits at the height
201
+ // of a strip that is gone. Review measured a removed 300px strip holding
202
+ // --pin-apps-bottom at 300px against a real stack bottom of 160px.
203
+ for (var name in last) {
204
+ if (last.hasOwnProperty(name) && !seen[name]) {
205
+ style.removeProperty('--pin-' + name + '-h');
206
+ style.removeProperty('--pin-' + name + '-bottom');
207
+ style.removeProperty('--pin-' + name + '-top');
208
+ delete last[name];
209
+ }
210
+ }
211
+ }
212
+
213
+ function publishAll() {
214
+ var pins = readPins();
215
+ write(pins, composed(pins));
216
+ syncObserver(pins);
217
+ }
218
+
219
+ // Observe whatever is pinned RIGHT NOW. observe() on an already-observed node
220
+ // is a no-op, so this only ever adds the new; unobserve() drops the departed
221
+ // so a replaced node cannot keep waking us from outside the document.
222
+ function syncObserver(pins) {
223
+ if (!pins.length) {
224
+ if (ro) { ro.disconnect(); ro = null; observed = []; }
225
+ return;
226
+ }
227
+ if (!ro) ro = new ResizeObserver(onResize);
228
+ var i, next = [];
229
+ for (i = 0; i < pins.length; i++) { next.push(pins[i].el); ro.observe(pins[i].el); }
230
+ for (i = 0; i < observed.length; i++) {
231
+ if (next.indexOf(observed[i]) === -1) ro.unobserve(observed[i]);
232
+ }
233
+ observed = next;
234
+ }
235
+
236
+ // PUBLISH IN THE ResizeObserver CALLBACK, SYNCHRONOUSLY. This is the whole
237
+ // fix, and it is a statement about WHEN a frame does its work rather than
238
+ // about what this code computes.
239
+ //
240
+ // A frame runs: rAF callbacks -> style/layout -> ResizeObserver callbacks ->
241
+ // paint. This publisher used to be woken by the observer and then DEFER the
242
+ // write to requestAnimationFrame, which lands at the top of the NEXT frame —
243
+ // so the frame in which a layer actually appeared or disappeared painted with
244
+ // the previous frame's number, every time. The RO callback runs after layout
245
+ // and before paint, which is exactly the last moment a write can still land
246
+ // in the frame that caused it.
247
+ //
248
+ // A/B measured in isolation, toggling a pinned layer's display and sampling
249
+ // what the changing frame paints: rAF-deferred wrong on 8/8 changing frames,
250
+ // RO-synchronous wrong on 0/8.
251
+ //
252
+ // This cannot loop. The values written here move CONSUMERS, and a consumer is
253
+ // not a pin — nothing this writes resizes anything it observes. A pinned layer
254
+ // that positions off --pin-<name>-top MOVES without RESIZING, and the observer
255
+ // fires on size, so it settles in one pass rather than ringing.
256
+ // The one way a SYNCHRONOUS publish could misbehave: a consumer that both
257
+ // reads a published value and is itself a pin, sized off what it reads
258
+ // (`height: calc(100vh - var(--pin-apps-top))`). That resizes an observed
259
+ // node from inside the observer's own callback, and left alone the browser
260
+ // reports "ResizeObserver loop completed with undelivered notifications" and
261
+ // drops the frame. So re-entry is counted, and past a couple of settling
262
+ // passes this hands the rest to the frame callback: the pathological page
263
+ // degrades to the OLD one-frame-late behaviour instead of wedging, and the
264
+ // ordinary page never reaches the branch. Reset on the next frame, so the
265
+ // count measures one frame's settling rather than the session's.
266
+ var depth = 0, resetQueued = false;
267
+ function onResize() {
268
+ if (depth >= 3) { schedule(); return; }
269
+ depth++;
270
+ if (!resetQueued) {
271
+ resetQueued = true;
272
+ window.requestAnimationFrame(function () { resetQueued = false; depth = 0; });
273
+ }
274
+ publishAll();
275
+ }
276
+
277
+ // SCROLL STILL NEEDS THE FRAME CALLBACK. A scroll changes a sticky header's
278
+ // bottom EDGE without changing its size, so the observer never sees it and
279
+ // there is nothing to be synchronous with — rAF is the right clock, and it
280
+ // coalesces the burst iOS momentum fires far above 60Hz.
281
+ function schedule() {
282
+ if (queued) return;
283
+ queued = true;
284
+ window.requestAnimationFrame(function () { queued = false; publishAll(); });
285
+ }
286
+
287
+ function invalidate() { forceWrite = true; publishAll(); }
288
+ document.addEventListener('DOMContentLoaded', invalidate);
289
+ document.addEventListener('turbo:load', invalidate);
290
+ // A Turbo Stream replaces nodes WITHOUT a turbo:load. The registry is derived
291
+ // per publish, so a stale reference is no longer possible either way — but the
292
+ // OBSERVER still has to be moved onto the incoming node, and the stack has to
293
+ // be recomposed for a layer that arrived or left. Both happen in publishAll.
294
+ document.addEventListener('turbo:before-stream-render', function () {
295
+ window.requestAnimationFrame(invalidate);
296
+ });
297
+ // Registered ONCE at start, not inside a re-scan, so a Turbo nav
298
+ // re-publishes instead of stacking another listener per visit.
299
+ window.addEventListener('scroll', schedule, { passive: true });
300
+ window.addEventListener('resize', schedule, { passive: true });
301
+
302
+ // A deferred module runs after the body is parsed, so the layers are already
303
+ // there: publish now rather than wait for DOMContentLoaded, which also
304
+ // covers a host that imports this after that event has fired.
305
+ invalidate();
306
+ }
@@ -0,0 +1,5 @@
1
+ // Stimulus 3.2.2, VENDORED from stimulus-rails 1.3.4 (app/assets/javascripts/stimulus.min.js),
2
+ // with its Sprockets link directives and source-map comment removed. Pinned as
3
+ // "@hotwired/stimulus" (config/importmap.rb); a host that pins the same name wins.
4
+ // Bumping is a deliberate act: replace the file and change the version here.
5
+ class e{constructor(e,t,s){this.eventTarget=e,this.eventName=t,this.eventOptions=s,this.unorderedBindings=new Set}connect(){this.eventTarget.addEventListener(this.eventName,this,this.eventOptions)}disconnect(){this.eventTarget.removeEventListener(this.eventName,this,this.eventOptions)}bindingConnected(e){this.unorderedBindings.add(e)}bindingDisconnected(e){this.unorderedBindings.delete(e)}handleEvent(e){const t=function(e){if("immediatePropagationStopped"in e)return e;{const{stopImmediatePropagation:t}=e;return Object.assign(e,{immediatePropagationStopped:!1,stopImmediatePropagation(){this.immediatePropagationStopped=!0,t.call(this)}})}}(e);for(const e of this.bindings){if(t.immediatePropagationStopped)break;e.handleEvent(t)}}hasBindings(){return this.unorderedBindings.size>0}get bindings(){return Array.from(this.unorderedBindings).sort(((e,t)=>{const s=e.index,r=t.index;return s<r?-1:s>r?1:0}))}}class t{constructor(e){this.application=e,this.eventListenerMaps=new Map,this.started=!1}start(){this.started||(this.started=!0,this.eventListeners.forEach((e=>e.connect())))}stop(){this.started&&(this.started=!1,this.eventListeners.forEach((e=>e.disconnect())))}get eventListeners(){return Array.from(this.eventListenerMaps.values()).reduce(((e,t)=>e.concat(Array.from(t.values()))),[])}bindingConnected(e){this.fetchEventListenerForBinding(e).bindingConnected(e)}bindingDisconnected(e,t=!1){this.fetchEventListenerForBinding(e).bindingDisconnected(e),t&&this.clearEventListenersForBinding(e)}handleError(e,t,s={}){this.application.handleError(e,`Error ${t}`,s)}clearEventListenersForBinding(e){const t=this.fetchEventListenerForBinding(e);t.hasBindings()||(t.disconnect(),this.removeMappedEventListenerFor(e))}removeMappedEventListenerFor(e){const{eventTarget:t,eventName:s,eventOptions:r}=e,n=this.fetchEventListenerMapForEventTarget(t),i=this.cacheKey(s,r);n.delete(i),0==n.size&&this.eventListenerMaps.delete(t)}fetchEventListenerForBinding(e){const{eventTarget:t,eventName:s,eventOptions:r}=e;return this.fetchEventListener(t,s,r)}fetchEventListener(e,t,s){const r=this.fetchEventListenerMapForEventTarget(e),n=this.cacheKey(t,s);let i=r.get(n);return i||(i=this.createEventListener(e,t,s),r.set(n,i)),i}createEventListener(t,s,r){const n=new e(t,s,r);return this.started&&n.connect(),n}fetchEventListenerMapForEventTarget(e){let t=this.eventListenerMaps.get(e);return t||(t=new Map,this.eventListenerMaps.set(e,t)),t}cacheKey(e,t){const s=[e];return Object.keys(t).sort().forEach((e=>{s.push(`${t[e]?"":"!"}${e}`)})),s.join(":")}}const s={stop:({event:e,value:t})=>(t&&e.stopPropagation(),!0),prevent:({event:e,value:t})=>(t&&e.preventDefault(),!0),self:({event:e,value:t,element:s})=>!t||s===e.target},r=/^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;function n(e){return"window"==e?window:"document"==e?document:void 0}function i(e){return e.replace(/(?:[_-])([a-z0-9])/g,((e,t)=>t.toUpperCase()))}function o(e){return i(e.replace(/--/g,"-").replace(/__/g,"_"))}function a(e){return e.charAt(0).toUpperCase()+e.slice(1)}function c(e){return e.replace(/([A-Z])/g,((e,t)=>`-${t.toLowerCase()}`))}function l(e){return null!=e}function h(e,t){return Object.prototype.hasOwnProperty.call(e,t)}const u=["meta","ctrl","alt","shift"];const d={a:()=>"click",button:()=>"click",form:()=>"submit",details:()=>"toggle",input:e=>"submit"==e.getAttribute("type")?"click":"input",select:()=>"change",textarea:()=>"input"};function m(e){throw new Error(e)}function g(e){try{return JSON.parse(e)}catch(t){return e}}class p{constructor(e,t){this.context=e,this.action=t}get index(){return this.action.index}get eventTarget(){return this.action.eventTarget}get eventOptions(){return this.action.eventOptions}get identifier(){return this.context.identifier}handleEvent(e){const t=this.prepareActionEvent(e);this.willBeInvokedByEvent(e)&&this.applyEventModifiers(t)&&this.invokeWithEvent(t)}get eventName(){return this.action.eventName}get method(){const e=this.controller[this.methodName];if("function"==typeof e)return e;throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`)}applyEventModifiers(e){const{element:t}=this.action,{actionDescriptorFilters:s}=this.context.application,{controller:r}=this.context;let n=!0;for(const[i,o]of Object.entries(this.eventOptions))if(i in s){const a=s[i];n=n&&a({name:i,value:o,event:e,element:t,controller:r})}return n}prepareActionEvent(e){return Object.assign(e,{params:this.action.params})}invokeWithEvent(e){const{target:t,currentTarget:s}=e;try{this.method.call(this.controller,e),this.context.logDebugActivity(this.methodName,{event:e,target:t,currentTarget:s,action:this.methodName})}catch(t){const{identifier:s,controller:r,element:n,index:i}=this,o={identifier:s,controller:r,element:n,index:i,event:e};this.context.handleError(t,`invoking action "${this.action}"`,o)}}willBeInvokedByEvent(e){const t=e.target;return!(e instanceof KeyboardEvent&&this.action.shouldIgnoreKeyboardEvent(e))&&(!(e instanceof MouseEvent&&this.action.shouldIgnoreMouseEvent(e))&&(this.element===t||(t instanceof Element&&this.element.contains(t)?this.scope.containsElement(t):this.scope.containsElement(this.action.element))))}get controller(){return this.context.controller}get methodName(){return this.action.methodName}get element(){return this.scope.element}get scope(){return this.context.scope}}class f{constructor(e,t){this.mutationObserverInit={attributes:!0,childList:!0,subtree:!0},this.element=e,this.started=!1,this.delegate=t,this.elements=new Set,this.mutationObserver=new MutationObserver((e=>this.processMutations(e)))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,this.mutationObserverInit),this.refresh())}pause(e){this.started&&(this.mutationObserver.disconnect(),this.started=!1),e(),this.started||(this.mutationObserver.observe(this.element,this.mutationObserverInit),this.started=!0)}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started){const e=new Set(this.matchElementsInTree());for(const t of Array.from(this.elements))e.has(t)||this.removeElement(t);for(const t of Array.from(e))this.addElement(t)}}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){"attributes"==e.type?this.processAttributeChange(e.target,e.attributeName):"childList"==e.type&&(this.processRemovedNodes(e.removedNodes),this.processAddedNodes(e.addedNodes))}processAttributeChange(e,t){this.elements.has(e)?this.delegate.elementAttributeChanged&&this.matchElement(e)?this.delegate.elementAttributeChanged(e,t):this.removeElement(e):this.matchElement(e)&&this.addElement(e)}processRemovedNodes(e){for(const t of Array.from(e)){const e=this.elementFromNode(t);e&&this.processTree(e,this.removeElement)}}processAddedNodes(e){for(const t of Array.from(e)){const e=this.elementFromNode(t);e&&this.elementIsActive(e)&&this.processTree(e,this.addElement)}}matchElement(e){return this.delegate.matchElement(e)}matchElementsInTree(e=this.element){return this.delegate.matchElementsInTree(e)}processTree(e,t){for(const s of this.matchElementsInTree(e))t.call(this,s)}elementFromNode(e){if(e.nodeType==Node.ELEMENT_NODE)return e}elementIsActive(e){return e.isConnected==this.element.isConnected&&this.element.contains(e)}addElement(e){this.elements.has(e)||this.elementIsActive(e)&&(this.elements.add(e),this.delegate.elementMatched&&this.delegate.elementMatched(e))}removeElement(e){this.elements.has(e)&&(this.elements.delete(e),this.delegate.elementUnmatched&&this.delegate.elementUnmatched(e))}}class b{constructor(e,t,s){this.attributeName=t,this.delegate=s,this.elementObserver=new f(e,this)}get element(){return this.elementObserver.element}get selector(){return`[${this.attributeName}]`}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get started(){return this.elementObserver.started}matchElement(e){return e.hasAttribute(this.attributeName)}matchElementsInTree(e){const t=this.matchElement(e)?[e]:[],s=Array.from(e.querySelectorAll(this.selector));return t.concat(s)}elementMatched(e){this.delegate.elementMatchedAttribute&&this.delegate.elementMatchedAttribute(e,this.attributeName)}elementUnmatched(e){this.delegate.elementUnmatchedAttribute&&this.delegate.elementUnmatchedAttribute(e,this.attributeName)}elementAttributeChanged(e,t){this.delegate.elementAttributeValueChanged&&this.attributeName==t&&this.delegate.elementAttributeValueChanged(e,t)}}function v(e,t,s){O(e,t).add(s)}function y(e,t,s){O(e,t).delete(s),A(e,t)}function O(e,t){let s=e.get(t);return s||(s=new Set,e.set(t,s)),s}function A(e,t){const s=e.get(t);null!=s&&0==s.size&&e.delete(t)}class E{constructor(){this.valuesByKey=new Map}get keys(){return Array.from(this.valuesByKey.keys())}get values(){return Array.from(this.valuesByKey.values()).reduce(((e,t)=>e.concat(Array.from(t))),[])}get size(){return Array.from(this.valuesByKey.values()).reduce(((e,t)=>e+t.size),0)}add(e,t){v(this.valuesByKey,e,t)}delete(e,t){y(this.valuesByKey,e,t)}has(e,t){const s=this.valuesByKey.get(e);return null!=s&&s.has(t)}hasKey(e){return this.valuesByKey.has(e)}hasValue(e){return Array.from(this.valuesByKey.values()).some((t=>t.has(e)))}getValuesForKey(e){const t=this.valuesByKey.get(e);return t?Array.from(t):[]}getKeysForValue(e){return Array.from(this.valuesByKey).filter((([t,s])=>s.has(e))).map((([e,t])=>e))}}class w extends E{constructor(){super(),this.keysByValue=new Map}get values(){return Array.from(this.keysByValue.keys())}add(e,t){super.add(e,t),v(this.keysByValue,t,e)}delete(e,t){super.delete(e,t),y(this.keysByValue,t,e)}hasValue(e){return this.keysByValue.has(e)}getKeysForValue(e){const t=this.keysByValue.get(e);return t?Array.from(t):[]}}class M{constructor(e,t,s,r){this._selector=t,this.details=r,this.elementObserver=new f(e,this),this.delegate=s,this.matchesByElement=new E}get started(){return this.elementObserver.started}get selector(){return this._selector}set selector(e){this._selector=e,this.refresh()}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get element(){return this.elementObserver.element}matchElement(e){const{selector:t}=this;if(t){const s=e.matches(t);return this.delegate.selectorMatchElement?s&&this.delegate.selectorMatchElement(e,this.details):s}return!1}matchElementsInTree(e){const{selector:t}=this;if(t){const s=this.matchElement(e)?[e]:[],r=Array.from(e.querySelectorAll(t)).filter((e=>this.matchElement(e)));return s.concat(r)}return[]}elementMatched(e){const{selector:t}=this;t&&this.selectorMatched(e,t)}elementUnmatched(e){const t=this.matchesByElement.getKeysForValue(e);for(const s of t)this.selectorUnmatched(e,s)}elementAttributeChanged(e,t){const{selector:s}=this;if(s){const t=this.matchElement(e),r=this.matchesByElement.has(s,e);t&&!r?this.selectorMatched(e,s):!t&&r&&this.selectorUnmatched(e,s)}}selectorMatched(e,t){this.delegate.selectorMatched(e,t,this.details),this.matchesByElement.add(t,e)}selectorUnmatched(e,t){this.delegate.selectorUnmatched(e,t,this.details),this.matchesByElement.delete(t,e)}}class k{constructor(e,t){this.element=e,this.delegate=t,this.started=!1,this.stringMap=new Map,this.mutationObserver=new MutationObserver((e=>this.processMutations(e)))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,{attributes:!0,attributeOldValue:!0}),this.refresh())}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started)for(const e of this.knownAttributeNames)this.refreshAttribute(e,null)}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){const t=e.attributeName;t&&this.refreshAttribute(t,e.oldValue)}refreshAttribute(e,t){const s=this.delegate.getStringMapKeyForAttribute(e);if(null!=s){this.stringMap.has(e)||this.stringMapKeyAdded(s,e);const r=this.element.getAttribute(e);if(this.stringMap.get(e)!=r&&this.stringMapValueChanged(r,s,t),null==r){const t=this.stringMap.get(e);this.stringMap.delete(e),t&&this.stringMapKeyRemoved(s,e,t)}else this.stringMap.set(e,r)}}stringMapKeyAdded(e,t){this.delegate.stringMapKeyAdded&&this.delegate.stringMapKeyAdded(e,t)}stringMapValueChanged(e,t,s){this.delegate.stringMapValueChanged&&this.delegate.stringMapValueChanged(e,t,s)}stringMapKeyRemoved(e,t,s){this.delegate.stringMapKeyRemoved&&this.delegate.stringMapKeyRemoved(e,t,s)}get knownAttributeNames(){return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)))}get currentAttributeNames(){return Array.from(this.element.attributes).map((e=>e.name))}get recordedAttributeNames(){return Array.from(this.stringMap.keys())}}class N{constructor(e,t,s){this.attributeObserver=new b(e,t,this),this.delegate=s,this.tokensByElement=new E}get started(){return this.attributeObserver.started}start(){this.attributeObserver.start()}pause(e){this.attributeObserver.pause(e)}stop(){this.attributeObserver.stop()}refresh(){this.attributeObserver.refresh()}get element(){return this.attributeObserver.element}get attributeName(){return this.attributeObserver.attributeName}elementMatchedAttribute(e){this.tokensMatched(this.readTokensForElement(e))}elementAttributeValueChanged(e){const[t,s]=this.refreshTokensForElement(e);this.tokensUnmatched(t),this.tokensMatched(s)}elementUnmatchedAttribute(e){this.tokensUnmatched(this.tokensByElement.getValuesForKey(e))}tokensMatched(e){e.forEach((e=>this.tokenMatched(e)))}tokensUnmatched(e){e.forEach((e=>this.tokenUnmatched(e)))}tokenMatched(e){this.delegate.tokenMatched(e),this.tokensByElement.add(e.element,e)}tokenUnmatched(e){this.delegate.tokenUnmatched(e),this.tokensByElement.delete(e.element,e)}refreshTokensForElement(e){const t=this.tokensByElement.getValuesForKey(e),s=this.readTokensForElement(e),r=function(e,t){const s=Math.max(e.length,t.length);return Array.from({length:s},((s,r)=>[e[r],t[r]]))}(t,s).findIndex((([e,t])=>{return r=t,!((s=e)&&r&&s.index==r.index&&s.content==r.content);var s,r}));return-1==r?[[],[]]:[t.slice(r),s.slice(r)]}readTokensForElement(e){const t=this.attributeName;return function(e,t,s){return e.trim().split(/\s+/).filter((e=>e.length)).map(((e,r)=>({element:t,attributeName:s,content:e,index:r})))}(e.getAttribute(t)||"",e,t)}}class F{constructor(e,t,s){this.tokenListObserver=new N(e,t,this),this.delegate=s,this.parseResultsByToken=new WeakMap,this.valuesByTokenByElement=new WeakMap}get started(){return this.tokenListObserver.started}start(){this.tokenListObserver.start()}stop(){this.tokenListObserver.stop()}refresh(){this.tokenListObserver.refresh()}get element(){return this.tokenListObserver.element}get attributeName(){return this.tokenListObserver.attributeName}tokenMatched(e){const{element:t}=e,{value:s}=this.fetchParseResultForToken(e);s&&(this.fetchValuesByTokenForElement(t).set(e,s),this.delegate.elementMatchedValue(t,s))}tokenUnmatched(e){const{element:t}=e,{value:s}=this.fetchParseResultForToken(e);s&&(this.fetchValuesByTokenForElement(t).delete(e),this.delegate.elementUnmatchedValue(t,s))}fetchParseResultForToken(e){let t=this.parseResultsByToken.get(e);return t||(t=this.parseToken(e),this.parseResultsByToken.set(e,t)),t}fetchValuesByTokenForElement(e){let t=this.valuesByTokenByElement.get(e);return t||(t=new Map,this.valuesByTokenByElement.set(e,t)),t}parseToken(e){try{return{value:this.delegate.parseValueForToken(e)}}catch(e){return{error:e}}}}class B{constructor(e,t){this.context=e,this.delegate=t,this.bindingsByAction=new Map}start(){this.valueListObserver||(this.valueListObserver=new F(this.element,this.actionAttribute,this),this.valueListObserver.start())}stop(){this.valueListObserver&&(this.valueListObserver.stop(),delete this.valueListObserver,this.disconnectAllActions())}get element(){return this.context.element}get identifier(){return this.context.identifier}get actionAttribute(){return this.schema.actionAttribute}get schema(){return this.context.schema}get bindings(){return Array.from(this.bindingsByAction.values())}connectAction(e){const t=new p(this.context,e);this.bindingsByAction.set(e,t),this.delegate.bindingConnected(t)}disconnectAction(e){const t=this.bindingsByAction.get(e);t&&(this.bindingsByAction.delete(e),this.delegate.bindingDisconnected(t))}disconnectAllActions(){this.bindings.forEach((e=>this.delegate.bindingDisconnected(e,!0))),this.bindingsByAction.clear()}parseValueForToken(e){const t=class{constructor(e,t,s,r){this.element=e,this.index=t,this.eventTarget=s.eventTarget||e,this.eventName=s.eventName||function(e){const t=e.tagName.toLowerCase();if(t in d)return d[t](e)}(e)||m("missing event name"),this.eventOptions=s.eventOptions||{},this.identifier=s.identifier||m("missing identifier"),this.methodName=s.methodName||m("missing method name"),this.keyFilter=s.keyFilter||"",this.schema=r}static forToken(e,t){return new this(e.element,e.index,function(e){const t=e.trim().match(r)||[];let s=t[2],i=t[3];return i&&!["keydown","keyup","keypress"].includes(s)&&(s+=`.${i}`,i=""),{eventTarget:n(t[4]),eventName:s,eventOptions:t[7]?(o=t[7],o.split(":").reduce(((e,t)=>Object.assign(e,{[t.replace(/^!/,"")]:!/^!/.test(t)})),{})):{},identifier:t[5],methodName:t[6],keyFilter:t[1]||i};var o}(e.content),t)}toString(){const e=this.keyFilter?`.${this.keyFilter}`:"",t=this.eventTargetName?`@${this.eventTargetName}`:"";return`${this.eventName}${e}${t}->${this.identifier}#${this.methodName}`}shouldIgnoreKeyboardEvent(e){if(!this.keyFilter)return!1;const t=this.keyFilter.split("+");if(this.keyFilterDissatisfied(e,t))return!0;const s=t.filter((e=>!u.includes(e)))[0];return!!s&&(h(this.keyMappings,s)||m(`contains unknown key filter: ${this.keyFilter}`),this.keyMappings[s].toLowerCase()!==e.key.toLowerCase())}shouldIgnoreMouseEvent(e){if(!this.keyFilter)return!1;const t=[this.keyFilter];return!!this.keyFilterDissatisfied(e,t)}get params(){const e={},t=new RegExp(`^data-${this.identifier}-(.+)-param$`,"i");for(const{name:s,value:r}of Array.from(this.element.attributes)){const n=s.match(t),o=n&&n[1];o&&(e[i(o)]=g(r))}return e}get eventTargetName(){return(e=this.eventTarget)==window?"window":e==document?"document":void 0;var e}get keyMappings(){return this.schema.keyMappings}keyFilterDissatisfied(e,t){const[s,r,n,i]=u.map((e=>t.includes(e)));return e.metaKey!==s||e.ctrlKey!==r||e.altKey!==n||e.shiftKey!==i}}.forToken(e,this.schema);if(t.identifier==this.identifier)return t}elementMatchedValue(e,t){this.connectAction(t)}elementUnmatchedValue(e,t){this.disconnectAction(t)}}class C{constructor(e,t){this.context=e,this.receiver=t,this.stringMapObserver=new k(this.element,this),this.valueDescriptorMap=this.controller.valueDescriptorMap}start(){this.stringMapObserver.start(),this.invokeChangedCallbacksForDefaultValues()}stop(){this.stringMapObserver.stop()}get element(){return this.context.element}get controller(){return this.context.controller}getStringMapKeyForAttribute(e){if(e in this.valueDescriptorMap)return this.valueDescriptorMap[e].name}stringMapKeyAdded(e,t){const s=this.valueDescriptorMap[t];this.hasValue(e)||this.invokeChangedCallback(e,s.writer(this.receiver[e]),s.writer(s.defaultValue))}stringMapValueChanged(e,t,s){const r=this.valueDescriptorNameMap[t];null!==e&&(null===s&&(s=r.writer(r.defaultValue)),this.invokeChangedCallback(t,e,s))}stringMapKeyRemoved(e,t,s){const r=this.valueDescriptorNameMap[e];this.hasValue(e)?this.invokeChangedCallback(e,r.writer(this.receiver[e]),s):this.invokeChangedCallback(e,r.writer(r.defaultValue),s)}invokeChangedCallbacksForDefaultValues(){for(const{key:e,name:t,defaultValue:s,writer:r}of this.valueDescriptors)null==s||this.controller.data.has(e)||this.invokeChangedCallback(t,r(s),void 0)}invokeChangedCallback(e,t,s){const r=`${e}Changed`,n=this.receiver[r];if("function"==typeof n){const r=this.valueDescriptorNameMap[e];try{const e=r.reader(t);let i=s;s&&(i=r.reader(s)),n.call(this.receiver,e,i)}catch(e){throw e instanceof TypeError&&(e.message=`Stimulus Value "${this.context.identifier}.${r.name}" - ${e.message}`),e}}}get valueDescriptors(){const{valueDescriptorMap:e}=this;return Object.keys(e).map((t=>e[t]))}get valueDescriptorNameMap(){const e={};return Object.keys(this.valueDescriptorMap).forEach((t=>{const s=this.valueDescriptorMap[t];e[s.name]=s})),e}hasValue(e){const t=`has${a(this.valueDescriptorNameMap[e].name)}`;return this.receiver[t]}}class ${constructor(e,t){this.context=e,this.delegate=t,this.targetsByName=new E}start(){this.tokenListObserver||(this.tokenListObserver=new N(this.element,this.attributeName,this),this.tokenListObserver.start())}stop(){this.tokenListObserver&&(this.disconnectAllTargets(),this.tokenListObserver.stop(),delete this.tokenListObserver)}tokenMatched({element:e,content:t}){this.scope.containsElement(e)&&this.connectTarget(e,t)}tokenUnmatched({element:e,content:t}){this.disconnectTarget(e,t)}connectTarget(e,t){var s;this.targetsByName.has(t,e)||(this.targetsByName.add(t,e),null===(s=this.tokenListObserver)||void 0===s||s.pause((()=>this.delegate.targetConnected(e,t))))}disconnectTarget(e,t){var s;this.targetsByName.has(t,e)&&(this.targetsByName.delete(t,e),null===(s=this.tokenListObserver)||void 0===s||s.pause((()=>this.delegate.targetDisconnected(e,t))))}disconnectAllTargets(){for(const e of this.targetsByName.keys)for(const t of this.targetsByName.getValuesForKey(e))this.disconnectTarget(t,e)}get attributeName(){return`data-${this.context.identifier}-target`}get element(){return this.context.element}get scope(){return this.context.scope}}function T(e,t){const s=x(e);return Array.from(s.reduce(((e,s)=>(function(e,t){const s=e[t];return Array.isArray(s)?s:[]}(s,t).forEach((t=>e.add(t))),e)),new Set))}function S(e,t){return x(e).reduce(((e,s)=>(e.push(...function(e,t){const s=e[t];return s?Object.keys(s).map((e=>[e,s[e]])):[]}(s,t)),e)),[])}function x(e){const t=[];for(;e;)t.push(e),e=Object.getPrototypeOf(e);return t.reverse()}class D{constructor(e,t){this.started=!1,this.context=e,this.delegate=t,this.outletsByName=new E,this.outletElementsByName=new E,this.selectorObserverMap=new Map,this.attributeObserverMap=new Map}start(){this.started||(this.outletDefinitions.forEach((e=>{this.setupSelectorObserverForOutlet(e),this.setupAttributeObserverForOutlet(e)})),this.started=!0,this.dependentContexts.forEach((e=>e.refresh())))}refresh(){this.selectorObserverMap.forEach((e=>e.refresh())),this.attributeObserverMap.forEach((e=>e.refresh()))}stop(){this.started&&(this.started=!1,this.disconnectAllOutlets(),this.stopSelectorObservers(),this.stopAttributeObservers())}stopSelectorObservers(){this.selectorObserverMap.size>0&&(this.selectorObserverMap.forEach((e=>e.stop())),this.selectorObserverMap.clear())}stopAttributeObservers(){this.attributeObserverMap.size>0&&(this.attributeObserverMap.forEach((e=>e.stop())),this.attributeObserverMap.clear())}selectorMatched(e,t,{outletName:s}){const r=this.getOutlet(e,s);r&&this.connectOutlet(r,e,s)}selectorUnmatched(e,t,{outletName:s}){const r=this.getOutletFromMap(e,s);r&&this.disconnectOutlet(r,e,s)}selectorMatchElement(e,{outletName:t}){const s=this.selector(t),r=this.hasOutlet(e,t),n=e.matches(`[${this.schema.controllerAttribute}~=${t}]`);return!!s&&(r&&n&&e.matches(s))}elementMatchedAttribute(e,t){const s=this.getOutletNameFromOutletAttributeName(t);s&&this.updateSelectorObserverForOutlet(s)}elementAttributeValueChanged(e,t){const s=this.getOutletNameFromOutletAttributeName(t);s&&this.updateSelectorObserverForOutlet(s)}elementUnmatchedAttribute(e,t){const s=this.getOutletNameFromOutletAttributeName(t);s&&this.updateSelectorObserverForOutlet(s)}connectOutlet(e,t,s){var r;this.outletElementsByName.has(s,t)||(this.outletsByName.add(s,e),this.outletElementsByName.add(s,t),null===(r=this.selectorObserverMap.get(s))||void 0===r||r.pause((()=>this.delegate.outletConnected(e,t,s))))}disconnectOutlet(e,t,s){var r;this.outletElementsByName.has(s,t)&&(this.outletsByName.delete(s,e),this.outletElementsByName.delete(s,t),null===(r=this.selectorObserverMap.get(s))||void 0===r||r.pause((()=>this.delegate.outletDisconnected(e,t,s))))}disconnectAllOutlets(){for(const e of this.outletElementsByName.keys)for(const t of this.outletElementsByName.getValuesForKey(e))for(const s of this.outletsByName.getValuesForKey(e))this.disconnectOutlet(s,t,e)}updateSelectorObserverForOutlet(e){const t=this.selectorObserverMap.get(e);t&&(t.selector=this.selector(e))}setupSelectorObserverForOutlet(e){const t=this.selector(e),s=new M(document.body,t,this,{outletName:e});this.selectorObserverMap.set(e,s),s.start()}setupAttributeObserverForOutlet(e){const t=this.attributeNameForOutletName(e),s=new b(this.scope.element,t,this);this.attributeObserverMap.set(e,s),s.start()}selector(e){return this.scope.outlets.getSelectorForOutletName(e)}attributeNameForOutletName(e){return this.scope.schema.outletAttributeForScope(this.identifier,e)}getOutletNameFromOutletAttributeName(e){return this.outletDefinitions.find((t=>this.attributeNameForOutletName(t)===e))}get outletDependencies(){const e=new E;return this.router.modules.forEach((t=>{T(t.definition.controllerConstructor,"outlets").forEach((s=>e.add(s,t.identifier)))})),e}get outletDefinitions(){return this.outletDependencies.getKeysForValue(this.identifier)}get dependentControllerIdentifiers(){return this.outletDependencies.getValuesForKey(this.identifier)}get dependentContexts(){const e=this.dependentControllerIdentifiers;return this.router.contexts.filter((t=>e.includes(t.identifier)))}hasOutlet(e,t){return!!this.getOutlet(e,t)||!!this.getOutletFromMap(e,t)}getOutlet(e,t){return this.application.getControllerForElementAndIdentifier(e,t)}getOutletFromMap(e,t){return this.outletsByName.getValuesForKey(t).find((t=>t.element===e))}get scope(){return this.context.scope}get schema(){return this.context.schema}get identifier(){return this.context.identifier}get application(){return this.context.application}get router(){return this.application.router}}class L{constructor(e,t){this.logDebugActivity=(e,t={})=>{const{identifier:s,controller:r,element:n}=this;t=Object.assign({identifier:s,controller:r,element:n},t),this.application.logDebugActivity(this.identifier,e,t)},this.module=e,this.scope=t,this.controller=new e.controllerConstructor(this),this.bindingObserver=new B(this,this.dispatcher),this.valueObserver=new C(this,this.controller),this.targetObserver=new $(this,this),this.outletObserver=new D(this,this);try{this.controller.initialize(),this.logDebugActivity("initialize")}catch(e){this.handleError(e,"initializing controller")}}connect(){this.bindingObserver.start(),this.valueObserver.start(),this.targetObserver.start(),this.outletObserver.start();try{this.controller.connect(),this.logDebugActivity("connect")}catch(e){this.handleError(e,"connecting controller")}}refresh(){this.outletObserver.refresh()}disconnect(){try{this.controller.disconnect(),this.logDebugActivity("disconnect")}catch(e){this.handleError(e,"disconnecting controller")}this.outletObserver.stop(),this.targetObserver.stop(),this.valueObserver.stop(),this.bindingObserver.stop()}get application(){return this.module.application}get identifier(){return this.module.identifier}get schema(){return this.application.schema}get dispatcher(){return this.application.dispatcher}get element(){return this.scope.element}get parentElement(){return this.element.parentElement}handleError(e,t,s={}){const{identifier:r,controller:n,element:i}=this;s=Object.assign({identifier:r,controller:n,element:i},s),this.application.handleError(e,`Error ${t}`,s)}targetConnected(e,t){this.invokeControllerMethod(`${t}TargetConnected`,e)}targetDisconnected(e,t){this.invokeControllerMethod(`${t}TargetDisconnected`,e)}outletConnected(e,t,s){this.invokeControllerMethod(`${o(s)}OutletConnected`,e,t)}outletDisconnected(e,t,s){this.invokeControllerMethod(`${o(s)}OutletDisconnected`,e,t)}invokeControllerMethod(e,...t){const s=this.controller;"function"==typeof s[e]&&s[e](...t)}}function V(e){return function(e,t){const s=I(e),r=function(e,t){return K(t).reduce(((s,r)=>{const n=function(e,t,s){const r=Object.getOwnPropertyDescriptor(e,s);if(!r||!("value"in r)){const e=Object.getOwnPropertyDescriptor(t,s).value;return r&&(e.get=r.get||e.get,e.set=r.set||e.set),e}}(e,t,r);return n&&Object.assign(s,{[r]:n}),s}),{})}(e.prototype,t);return Object.defineProperties(s.prototype,r),s}(e,function(e){return T(e,"blessings").reduce(((t,s)=>{const r=s(e);for(const e in r){const s=t[e]||{};t[e]=Object.assign(s,r[e])}return t}),{})}(e))}const K="function"==typeof Object.getOwnPropertySymbols?e=>[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)]:Object.getOwnPropertyNames,I=(()=>{function e(e){function t(){return Reflect.construct(e,arguments,new.target)}return t.prototype=Object.create(e.prototype,{constructor:{value:t}}),Reflect.setPrototypeOf(t,e),t}try{return function(){const t=e((function(){this.a.call(this)}));t.prototype.a=function(){},new t}(),e}catch(e){return e=>class extends e{}}})();class j{constructor(e,t){this.application=e,this.definition=function(e){return{identifier:e.identifier,controllerConstructor:V(e.controllerConstructor)}}(t),this.contextsByScope=new WeakMap,this.connectedContexts=new Set}get identifier(){return this.definition.identifier}get controllerConstructor(){return this.definition.controllerConstructor}get contexts(){return Array.from(this.connectedContexts)}connectContextForScope(e){const t=this.fetchContextForScope(e);this.connectedContexts.add(t),t.connect()}disconnectContextForScope(e){const t=this.contextsByScope.get(e);t&&(this.connectedContexts.delete(t),t.disconnect())}fetchContextForScope(e){let t=this.contextsByScope.get(e);return t||(t=new L(this,e),this.contextsByScope.set(e,t)),t}}class U{constructor(e){this.scope=e}has(e){return this.data.has(this.getDataKey(e))}get(e){return this.getAll(e)[0]}getAll(e){const t=this.data.get(this.getDataKey(e))||"";return t.match(/[^\s]+/g)||[]}getAttributeName(e){return this.data.getAttributeNameForKey(this.getDataKey(e))}getDataKey(e){return`${e}-class`}get data(){return this.scope.data}}class P{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get(e){const t=this.getAttributeNameForKey(e);return this.element.getAttribute(t)}set(e,t){const s=this.getAttributeNameForKey(e);return this.element.setAttribute(s,t),this.get(e)}has(e){const t=this.getAttributeNameForKey(e);return this.element.hasAttribute(t)}delete(e){if(this.has(e)){const t=this.getAttributeNameForKey(e);return this.element.removeAttribute(t),!0}return!1}getAttributeNameForKey(e){return`data-${this.identifier}-${c(e)}`}}class R{constructor(e){this.warnedKeysByObject=new WeakMap,this.logger=e}warn(e,t,s){let r=this.warnedKeysByObject.get(e);r||(r=new Set,this.warnedKeysByObject.set(e,r)),r.has(t)||(r.add(t),this.logger.warn(s,e))}}function z(e,t){return`[${e}~="${t}"]`}class _{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce(((e,t)=>e||this.findTarget(t)||this.findLegacyTarget(t)),void 0)}findAll(...e){return e.reduce(((e,t)=>[...e,...this.findAllTargets(t),...this.findAllLegacyTargets(t)]),[])}findTarget(e){const t=this.getSelectorForTargetName(e);return this.scope.findElement(t)}findAllTargets(e){const t=this.getSelectorForTargetName(e);return this.scope.findAllElements(t)}getSelectorForTargetName(e){return z(this.schema.targetAttributeForScope(this.identifier),e)}findLegacyTarget(e){const t=this.getLegacySelectorForTargetName(e);return this.deprecate(this.scope.findElement(t),e)}findAllLegacyTargets(e){const t=this.getLegacySelectorForTargetName(e);return this.scope.findAllElements(t).map((t=>this.deprecate(t,e)))}getLegacySelectorForTargetName(e){const t=`${this.identifier}.${e}`;return z(this.schema.targetAttribute,t)}deprecate(e,t){if(e){const{identifier:s}=this,r=this.schema.targetAttribute,n=this.schema.targetAttributeForScope(s);this.guide.warn(e,`target:${t}`,`Please replace ${r}="${s}.${t}" with ${n}="${t}". The ${r} attribute is deprecated and will be removed in a future version of Stimulus.`)}return e}get guide(){return this.scope.guide}}class q{constructor(e,t){this.scope=e,this.controllerElement=t}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce(((e,t)=>e||this.findOutlet(t)),void 0)}findAll(...e){return e.reduce(((e,t)=>[...e,...this.findAllOutlets(t)]),[])}getSelectorForOutletName(e){const t=this.schema.outletAttributeForScope(this.identifier,e);return this.controllerElement.getAttribute(t)}findOutlet(e){const t=this.getSelectorForOutletName(e);if(t)return this.findElement(t,e)}findAllOutlets(e){const t=this.getSelectorForOutletName(e);return t?this.findAllElements(t,e):[]}findElement(e,t){return this.scope.queryElements(e).filter((s=>this.matchesElement(s,e,t)))[0]}findAllElements(e,t){return this.scope.queryElements(e).filter((s=>this.matchesElement(s,e,t)))}matchesElement(e,t,s){const r=e.getAttribute(this.scope.schema.controllerAttribute)||"";return e.matches(t)&&r.split(" ").includes(s)}}class W{constructor(e,t,s,r){this.targets=new _(this),this.classes=new U(this),this.data=new P(this),this.containsElement=e=>e.closest(this.controllerSelector)===this.element,this.schema=e,this.element=t,this.identifier=s,this.guide=new R(r),this.outlets=new q(this.documentScope,t)}findElement(e){return this.element.matches(e)?this.element:this.queryElements(e).find(this.containsElement)}findAllElements(e){return[...this.element.matches(e)?[this.element]:[],...this.queryElements(e).filter(this.containsElement)]}queryElements(e){return Array.from(this.element.querySelectorAll(e))}get controllerSelector(){return z(this.schema.controllerAttribute,this.identifier)}get isDocumentScope(){return this.element===document.documentElement}get documentScope(){return this.isDocumentScope?this:new W(this.schema,document.documentElement,this.identifier,this.guide.logger)}}class J{constructor(e,t,s){this.element=e,this.schema=t,this.delegate=s,this.valueListObserver=new F(this.element,this.controllerAttribute,this),this.scopesByIdentifierByElement=new WeakMap,this.scopeReferenceCounts=new WeakMap}start(){this.valueListObserver.start()}stop(){this.valueListObserver.stop()}get controllerAttribute(){return this.schema.controllerAttribute}parseValueForToken(e){const{element:t,content:s}=e;return this.parseValueForElementAndIdentifier(t,s)}parseValueForElementAndIdentifier(e,t){const s=this.fetchScopesByIdentifierForElement(e);let r=s.get(t);return r||(r=this.delegate.createScopeForElementAndIdentifier(e,t),s.set(t,r)),r}elementMatchedValue(e,t){const s=(this.scopeReferenceCounts.get(t)||0)+1;this.scopeReferenceCounts.set(t,s),1==s&&this.delegate.scopeConnected(t)}elementUnmatchedValue(e,t){const s=this.scopeReferenceCounts.get(t);s&&(this.scopeReferenceCounts.set(t,s-1),1==s&&this.delegate.scopeDisconnected(t))}fetchScopesByIdentifierForElement(e){let t=this.scopesByIdentifierByElement.get(e);return t||(t=new Map,this.scopesByIdentifierByElement.set(e,t)),t}}class H{constructor(e){this.application=e,this.scopeObserver=new J(this.element,this.schema,this),this.scopesByIdentifier=new E,this.modulesByIdentifier=new Map}get element(){return this.application.element}get schema(){return this.application.schema}get logger(){return this.application.logger}get controllerAttribute(){return this.schema.controllerAttribute}get modules(){return Array.from(this.modulesByIdentifier.values())}get contexts(){return this.modules.reduce(((e,t)=>e.concat(t.contexts)),[])}start(){this.scopeObserver.start()}stop(){this.scopeObserver.stop()}loadDefinition(e){this.unloadIdentifier(e.identifier);const t=new j(this.application,e);this.connectModule(t);const s=e.controllerConstructor.afterLoad;s&&s.call(e.controllerConstructor,e.identifier,this.application)}unloadIdentifier(e){const t=this.modulesByIdentifier.get(e);t&&this.disconnectModule(t)}getContextForElementAndIdentifier(e,t){const s=this.modulesByIdentifier.get(t);if(s)return s.contexts.find((t=>t.element==e))}proposeToConnectScopeForElementAndIdentifier(e,t){const s=this.scopeObserver.parseValueForElementAndIdentifier(e,t);s?this.scopeObserver.elementMatchedValue(s.element,s):console.error(`Couldn't find or create scope for identifier: "${t}" and element:`,e)}handleError(e,t,s){this.application.handleError(e,t,s)}createScopeForElementAndIdentifier(e,t){return new W(this.schema,e,t,this.logger)}scopeConnected(e){this.scopesByIdentifier.add(e.identifier,e);const t=this.modulesByIdentifier.get(e.identifier);t&&t.connectContextForScope(e)}scopeDisconnected(e){this.scopesByIdentifier.delete(e.identifier,e);const t=this.modulesByIdentifier.get(e.identifier);t&&t.disconnectContextForScope(e)}connectModule(e){this.modulesByIdentifier.set(e.identifier,e);this.scopesByIdentifier.getValuesForKey(e.identifier).forEach((t=>e.connectContextForScope(t)))}disconnectModule(e){this.modulesByIdentifier.delete(e.identifier);this.scopesByIdentifier.getValuesForKey(e.identifier).forEach((t=>e.disconnectContextForScope(t)))}}const Z={controllerAttribute:"data-controller",actionAttribute:"data-action",targetAttribute:"data-target",targetAttributeForScope:e=>`data-${e}-target`,outletAttributeForScope:(e,t)=>`data-${e}-${t}-outlet`,keyMappings:Object.assign(Object.assign({enter:"Enter",tab:"Tab",esc:"Escape",space:" ",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",page_up:"PageUp",page_down:"PageDown"},G("abcdefghijklmnopqrstuvwxyz".split("").map((e=>[e,e])))),G("0123456789".split("").map((e=>[e,e]))))};function G(e){return e.reduce(((e,[t,s])=>Object.assign(Object.assign({},e),{[t]:s})),{})}class Q{constructor(e=document.documentElement,r=Z){this.logger=console,this.debug=!1,this.logDebugActivity=(e,t,s={})=>{this.debug&&this.logFormattedMessage(e,t,s)},this.element=e,this.schema=r,this.dispatcher=new t(this),this.router=new H(this),this.actionDescriptorFilters=Object.assign({},s)}static start(e,t){const s=new this(e,t);return s.start(),s}async start(){await new Promise((e=>{"loading"==document.readyState?document.addEventListener("DOMContentLoaded",(()=>e())):e()})),this.logDebugActivity("application","starting"),this.dispatcher.start(),this.router.start(),this.logDebugActivity("application","start")}stop(){this.logDebugActivity("application","stopping"),this.dispatcher.stop(),this.router.stop(),this.logDebugActivity("application","stop")}register(e,t){this.load({identifier:e,controllerConstructor:t})}registerActionOption(e,t){this.actionDescriptorFilters[e]=t}load(e,...t){(Array.isArray(e)?e:[e,...t]).forEach((e=>{e.controllerConstructor.shouldLoad&&this.router.loadDefinition(e)}))}unload(e,...t){(Array.isArray(e)?e:[e,...t]).forEach((e=>this.router.unloadIdentifier(e)))}get controllers(){return this.router.contexts.map((e=>e.controller))}getControllerForElementAndIdentifier(e,t){const s=this.router.getContextForElementAndIdentifier(e,t);return s?s.controller:null}handleError(e,t,s){var r;this.logger.error("%s\n\n%o\n\n%o",t,e,s),null===(r=window.onerror)||void 0===r||r.call(window,t,"",0,0,e)}logFormattedMessage(e,t,s={}){s=Object.assign({application:this},s),this.logger.groupCollapsed(`${e} #${t}`),this.logger.log("details:",Object.assign({},s)),this.logger.groupEnd()}}function X(e,t,s){return e.application.getControllerForElementAndIdentifier(t,s)}function Y(e,t,s){let r=X(e,t,s);return r||(e.application.router.proposeToConnectScopeForElementAndIdentifier(t,s),r=X(e,t,s),r||void 0)}function ee([e,t],s){return function(e){const{token:t,typeDefinition:s}=e,r=`${c(t)}-value`,n=function(e){const{controller:t,token:s,typeDefinition:r}=e,n=function(e){const{controller:t,token:s,typeObject:r}=e,n=l(r.type),i=l(r.default),o=n&&i,a=n&&!i,c=!n&&i,h=te(r.type),u=se(e.typeObject.default);if(a)return h;if(c)return u;if(h!==u){throw new Error(`The specified default value for the Stimulus Value "${t?`${t}.${s}`:s}" must match the defined type "${h}". The provided default value of "${r.default}" is of type "${u}".`)}if(o)return h}({controller:t,token:s,typeObject:r}),i=se(r),o=te(r),a=n||i||o;if(a)return a;throw new Error(`Unknown value type "${t?`${t}.${r}`:s}" for "${s}" value`)}(e);return{type:n,key:r,name:i(r),get defaultValue(){return function(e){const t=te(e);if(t)return re[t];const s=h(e,"default"),r=h(e,"type"),n=e;if(s)return n.default;if(r){const{type:e}=n,t=te(e);if(t)return re[t]}return e}(s)},get hasCustomDefaultValue(){return void 0!==se(s)},reader:ne[n],writer:ie[n]||ie.default}}({controller:s,token:e,typeDefinition:t})}function te(e){switch(e){case Array:return"array";case Boolean:return"boolean";case Number:return"number";case Object:return"object";case String:return"string"}}function se(e){switch(typeof e){case"boolean":return"boolean";case"number":return"number";case"string":return"string"}return Array.isArray(e)?"array":"[object Object]"===Object.prototype.toString.call(e)?"object":void 0}const re={get array(){return[]},boolean:!1,number:0,get object(){return{}},string:""},ne={array(e){const t=JSON.parse(e);if(!Array.isArray(t))throw new TypeError(`expected value of type "array" but instead got value "${e}" of type "${se(t)}"`);return t},boolean:e=>!("0"==e||"false"==String(e).toLowerCase()),number:e=>Number(e.replace(/_/g,"")),object(e){const t=JSON.parse(e);if(null===t||"object"!=typeof t||Array.isArray(t))throw new TypeError(`expected value of type "object" but instead got value "${e}" of type "${se(t)}"`);return t},string:e=>e},ie={default:function(e){return`${e}`},array:oe,object:oe};function oe(e){return JSON.stringify(e)}class ae{constructor(e){this.context=e}static get shouldLoad(){return!0}static afterLoad(e,t){}get application(){return this.context.application}get scope(){return this.context.scope}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get targets(){return this.scope.targets}get outlets(){return this.scope.outlets}get classes(){return this.scope.classes}get data(){return this.scope.data}initialize(){}connect(){}disconnect(){}dispatch(e,{target:t=this.element,detail:s={},prefix:r=this.identifier,bubbles:n=!0,cancelable:i=!0}={}){const o=new CustomEvent(r?`${r}:${e}`:e,{detail:s,bubbles:n,cancelable:i});return t.dispatchEvent(o),o}}ae.blessings=[function(e){return T(e,"classes").reduce(((e,t)=>{return Object.assign(e,{[`${s=t}Class`]:{get(){const{classes:e}=this;if(e.has(s))return e.get(s);{const t=e.getAttributeName(s);throw new Error(`Missing attribute "${t}"`)}}},[`${s}Classes`]:{get(){return this.classes.getAll(s)}},[`has${a(s)}Class`]:{get(){return this.classes.has(s)}}});var s}),{})},function(e){return T(e,"targets").reduce(((e,t)=>{return Object.assign(e,{[`${s=t}Target`]:{get(){const e=this.targets.find(s);if(e)return e;throw new Error(`Missing target element "${s}" for "${this.identifier}" controller`)}},[`${s}Targets`]:{get(){return this.targets.findAll(s)}},[`has${a(s)}Target`]:{get(){return this.targets.has(s)}}});var s}),{})},function(e){const t=S(e,"values"),s={valueDescriptorMap:{get(){return t.reduce(((e,t)=>{const s=ee(t,this.identifier),r=this.data.getAttributeNameForKey(s.key);return Object.assign(e,{[r]:s})}),{})}}};return t.reduce(((e,t)=>Object.assign(e,function(e,t){const s=ee(e,t),{key:r,name:n,reader:i,writer:o}=s;return{[n]:{get(){const e=this.data.get(r);return null!==e?i(e):s.defaultValue},set(e){void 0===e?this.data.delete(r):this.data.set(r,o(e))}},[`has${a(n)}`]:{get(){return this.data.has(r)||s.hasCustomDefaultValue}}}}(t))),s)},function(e){return T(e,"outlets").reduce(((e,t)=>Object.assign(e,function(e){const t=o(e);return{[`${t}Outlet`]:{get(){const t=this.outlets.find(e),s=this.outlets.getSelectorForOutletName(e);if(t){const s=Y(this,t,e);if(s)return s;throw new Error(`The provided outlet element is missing an outlet controller "${e}" instance for host controller "${this.identifier}"`)}throw new Error(`Missing outlet element "${e}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${s}".`)}},[`${t}Outlets`]:{get(){const t=this.outlets.findAll(e);return t.length>0?t.map((t=>{const s=Y(this,t,e);if(s)return s;console.warn(`The provided outlet element is missing an outlet controller "${e}" instance for host controller "${this.identifier}"`,t)})).filter((e=>e)):[]}},[`${t}OutletElement`]:{get(){const t=this.outlets.find(e),s=this.outlets.getSelectorForOutletName(e);if(t)return t;throw new Error(`Missing outlet element "${e}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${s}".`)}},[`${t}OutletElements`]:{get(){return this.outlets.findAll(e)}},[`has${a(t)}Outlet`]:{get(){return this.outlets.has(e)}}}}(t))),{})}],ae.targets=[],ae.outlets=[],ae.values={};export{Q as Application,b as AttributeObserver,L as Context,ae as Controller,f as ElementObserver,w as IndexedMultimap,E as Multimap,M as SelectorObserver,k as StringMapObserver,N as TokenListObserver,F as ValueListObserver,v as add,Z as defaultSchema,y as del,O as fetch,A as prune};
@@ -40,20 +40,27 @@
40
40
  logo_path = Studio.logo_for("Navbar Logo")
41
41
  %>
42
42
 
43
- <%# nav-shell is the --nav-p scope: navCollapse() (layouts/studio/_head) writes
43
+ <%# nav-shell is the --nav-p scope: the engine's nav-collapse controller (studio/nav_collapse) writes
44
44
  the collapse progress, 0 expanded .. 1 collapsed, on THIS element once per
45
45
  animation frame, and the band table below derives the row padding, logo size
46
46
  and title sizes from it with calc(). Nothing on the collapse path carries a
47
47
  time-based transition — the head's comment carries the measurements that
48
48
  bought that. transition-shadow stays: box-shadow paints, it never reflows,
49
49
  so it cannot move content under the reader.
50
- Preview keeps nav-shell (the calc()s have to resolve) but no x-data — the
51
- /navbar review page drives --nav-p from its own Scrolled toggle. %>
50
+ The controller toggles the scrolled classes (the shadow) when the scroll
51
+ passes its hysteresis.
52
+ Preview keeps nav-shell (the calc()s have to resolve) but no controller — the
53
+ /navbar review page drives --nav-p from its own Scrolled toggle, and its
54
+ :class reads that toggle's `scrolled`. %>
52
55
  <%# data-pin names this header as a layer of the pinned stack, so
53
56
  layouts/studio/_head publishes --pin-nav-h and --pin-nav-bottom for it and
54
57
  anything below can position off it in pure CSS. The legacy --nav-h /
55
58
  --nav-bottom keep publishing unchanged; this is additive. %>
56
- <header data-pin="nav" <%= 'x-data="navCollapse()"'.html_safe unless is_preview %>
59
+ <%# The bare x-data is the Alpine scope the header's descendants still bind
60
+ through ($store.devMode on the user nav, the sidebar trigger's @click):
61
+ Alpine initializes directives only inside a component, and the header was
62
+ that component while navCollapse() lived on it. %>
63
+ <header data-pin="nav"<% unless is_preview %> x-data data-studio-controller="nav-collapse" data-nav-collapse-scrolled-class="shadow-lg border-b border-subtle is-scrolled"<% end %>
57
64
  <%# top-0, a STATIC value, and never a custom property. Any bars render as
58
65
  this header's sibling in normal flow (studio/banners/_stack), so they
59
66
  already occupy their own height above it and there is nothing to
@@ -63,7 +70,7 @@
63
70
  during a view transition composited at two different tops. Only CSS
64
71
  can set this top now, so none of that is reachable. %>
65
72
  class="nav-shell <%= is_preview ? 'bg-page' : "#{'vt-pinned-header ' if pin_header}sticky top-0 z-50 bg-page transition-shadow duration-300" %>"
66
- :class="scrolled && 'shadow-lg border-b border-subtle is-scrolled'">
73
+ <%= %(:class="scrolled && 'shadow-lg border-b border-subtle is-scrolled'").html_safe if is_preview %>>
67
74
  <style>
68
75
  /* THE PHONE STEP IS A SHARE OF THE VIEWPORT, NOT A CONSTANT, and that is the
69
76
  whole width fix. 14rem is 224px — 57% of a 390px screen — handed to a
@@ -109,7 +116,8 @@
109
116
  was left to hand-write its own — the way four independent copies happened
110
117
  in the first place. It ships from engine.css now (every consuming app
111
118
  imports it), so a forking app OPTS IN with `nav-shell` +
112
- x-data="navCollapse()" and overrides only the endpoints that differ.
119
+ data-studio-controller="nav-collapse" (or x-data="navCollapse()",
120
+ the Alpine shim) and overrides only the endpoints that differ.
113
121
 
114
122
  THE OPT-IN IS NOT THE WHOLE JOB — this is the partial a forker actually
115
123
  reads, so the caveat belongs here too rather than only beside the band