turbo_reflex 0.0.6 → 0.0.8
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +10 -17
- data/README.md +1 -6
- data/app/assets/builds/turbo_reflex.js +1 -1
- data/app/assets/builds/turbo_reflex.js.map +4 -4
- data/app/controllers/concerns/turbo_reflex/controller.rb +12 -108
- data/app/javascript/activity.js +20 -0
- data/app/javascript/delegates.js +27 -0
- data/app/javascript/drivers/form.js +12 -0
- data/app/javascript/drivers/frame.js +11 -0
- data/app/javascript/drivers/index.js +63 -0
- data/app/javascript/drivers/window.js +50 -0
- data/app/javascript/elements.js +29 -50
- data/app/javascript/index.js +72 -0
- data/app/javascript/lifecycle.js +45 -0
- data/app/javascript/logger.js +34 -0
- data/app/javascript/renderer.js +23 -0
- data/app/javascript/schema.js +6 -0
- data/app/javascript/turbo.js +42 -0
- data/app/javascript/urls.js +9 -0
- data/app/javascript/uuids.js +10 -0
- data/lib/turbo_reflex/base.rb +59 -9
- data/lib/turbo_reflex/engine.rb +1 -9
- data/lib/turbo_reflex/runner.rb +202 -0
- data/lib/turbo_reflex/version.rb +1 -1
- data/package.json +5 -4
- data/turbo_reflex.gemspec +1 -0
- data/yarn.lock +135 -135
- metadata +31 -11
- data/app/controllers/turbo_reflex/turbo_reflexes_controller.rb +0 -12
- data/app/helpers/turbo_reflex/turbo_reflex_helper.rb +0 -9
- data/app/javascript/event_registry.js +0 -34
- data/app/javascript/frame_sources.js +0 -28
- data/app/javascript/lifecycle_events.js +0 -24
- data/app/javascript/security.js +0 -7
- data/app/javascript/turbo_reflex.js +0 -111
- data/config/routes.rb +0 -5
- data/lib/turbo_reflex/errors.rb +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a31a4a624ebc825d519458818b34735051ab4fefd4d03d8b3c974ec96cae27fd
|
|
4
|
+
data.tar.gz: 5e4df1345eeedcc08ef0ee5989575c38f38a4956495654f4f3416d596dd3c6b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3139e5ce67c4035913b8c4690273ccb591afc30a856492b0bae56565654c8c8037df9f22565750786cb24a7e6632c3dc717dbc0490f6cc8aad8fef879b2010c3
|
|
7
|
+
data.tar.gz: c43dd944ef5c0ac1e60150471f5b4a172fe400c2e5f2e57f98735474f07a0204b34cfdf2123a12a157661042d3b518a5f0a6003b0c68dfc1cd6ca02b805d221b
|
data/Gemfile.lock
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
turbo_reflex (0.0.
|
|
4
|
+
turbo_reflex (0.0.8)
|
|
5
5
|
rails (>= 6.1)
|
|
6
6
|
turbo-rails (>= 1.1)
|
|
7
|
+
turbo_ready (>= 0.1)
|
|
7
8
|
|
|
8
9
|
GEM
|
|
9
10
|
remote: https://rubygems.org/
|
|
@@ -97,7 +98,6 @@ GEM
|
|
|
97
98
|
cuprite (0.13)
|
|
98
99
|
capybara (>= 2.1, < 4)
|
|
99
100
|
ferrum (~> 0.11.0)
|
|
100
|
-
digest (3.1.0)
|
|
101
101
|
erubi (1.11.0)
|
|
102
102
|
ferrum (0.11)
|
|
103
103
|
addressable (~> 2.5)
|
|
@@ -129,20 +129,14 @@ GEM
|
|
|
129
129
|
builder
|
|
130
130
|
minitest (>= 5.0)
|
|
131
131
|
ruby-progressbar
|
|
132
|
-
net-imap (0.
|
|
133
|
-
digest
|
|
132
|
+
net-imap (0.3.1)
|
|
134
133
|
net-protocol
|
|
135
|
-
|
|
136
|
-
net-pop (0.1.1)
|
|
137
|
-
digest
|
|
134
|
+
net-pop (0.1.2)
|
|
138
135
|
net-protocol
|
|
139
|
-
timeout
|
|
140
136
|
net-protocol (0.1.3)
|
|
141
137
|
timeout
|
|
142
|
-
net-smtp (0.3.
|
|
143
|
-
digest
|
|
138
|
+
net-smtp (0.3.2)
|
|
144
139
|
net-protocol
|
|
145
|
-
timeout
|
|
146
140
|
nio4r (2.5.8)
|
|
147
141
|
nokogiri (1.13.8-aarch64-linux)
|
|
148
142
|
racc (~> 1.4)
|
|
@@ -212,7 +206,7 @@ GEM
|
|
|
212
206
|
rubocop-ast (>= 0.4.0)
|
|
213
207
|
ruby-progressbar (1.11.0)
|
|
214
208
|
rubyzip (2.3.2)
|
|
215
|
-
selenium-webdriver (4.
|
|
209
|
+
selenium-webdriver (4.5.0)
|
|
216
210
|
childprocess (>= 0.5, < 5.0)
|
|
217
211
|
rexml (~> 3.2, >= 3.2.5)
|
|
218
212
|
rubyzip (>= 1.2.2, < 3.0)
|
|
@@ -224,14 +218,13 @@ GEM
|
|
|
224
218
|
actionpack (>= 5.2)
|
|
225
219
|
activesupport (>= 5.2)
|
|
226
220
|
sprockets (>= 3.0.0)
|
|
227
|
-
sqlite3 (1.5.
|
|
228
|
-
sqlite3 (1.5.
|
|
221
|
+
sqlite3 (1.5.2-aarch64-linux)
|
|
222
|
+
sqlite3 (1.5.2-arm64-darwin)
|
|
229
223
|
standard (1.16.1)
|
|
230
224
|
rubocop (= 1.35.1)
|
|
231
225
|
rubocop-performance (= 1.14.3)
|
|
232
226
|
standardrb (1.0.1)
|
|
233
227
|
standard
|
|
234
|
-
strscan (3.0.4)
|
|
235
228
|
tailwindcss-rails (2.0.14-aarch64-linux)
|
|
236
229
|
railties (>= 6.0.0)
|
|
237
230
|
tailwindcss-rails (2.0.14-arm64-darwin)
|
|
@@ -253,7 +246,7 @@ GEM
|
|
|
253
246
|
activemodel (>= 6.0.0)
|
|
254
247
|
bindex (>= 0.4.0)
|
|
255
248
|
railties (>= 6.0.0)
|
|
256
|
-
webdrivers (5.
|
|
249
|
+
webdrivers (5.2.0)
|
|
257
250
|
nokogiri (~> 1.6)
|
|
258
251
|
rubyzip (>= 1.3.0)
|
|
259
252
|
selenium-webdriver (~> 4.0)
|
|
@@ -263,7 +256,7 @@ GEM
|
|
|
263
256
|
websocket-extensions (0.1.5)
|
|
264
257
|
xpath (3.2.0)
|
|
265
258
|
nokogiri (~> 1.8)
|
|
266
|
-
zeitwerk (2.6.
|
|
259
|
+
zeitwerk (2.6.1)
|
|
267
260
|
|
|
268
261
|
PLATFORMS
|
|
269
262
|
aarch64-linux
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</h1>
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
|
|
11
|
-
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-
|
|
11
|
+
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-694-47d299.svg" />
|
|
12
12
|
</a>
|
|
13
13
|
<a href="https://codeclimate.com/github/hopsoft/turbo_reflex/maintainability">
|
|
14
14
|
<img src="https://api.codeclimate.com/v1/badges/fe1162a742fe83a4fdfd/maintainability" />
|
|
@@ -145,7 +145,6 @@ TurboReflex is a lightweight Turbo Frame extension... which means that reactivit
|
|
|
145
145
|
+ <%= turbo_reflex_meta_tag %>
|
|
146
146
|
</head>
|
|
147
147
|
<body>
|
|
148
|
-
+ <%= turbo_reflex_frame_tag %>
|
|
149
148
|
</body>
|
|
150
149
|
</html>
|
|
151
150
|
```
|
|
@@ -207,12 +206,8 @@ TurboReflex.registerEvent('sl-change', ['sl-switch'])
|
|
|
207
206
|
|
|
208
207
|
TurboReflex supports the following lifecycle events.
|
|
209
208
|
|
|
210
|
-
- `turbo-reflex:before-start` - fires before reflex processing starts
|
|
211
209
|
- `turbo-reflex:start` - fires before the reflex is sent to the server
|
|
212
210
|
- `turbo-reflex:finish` - fires after the server has processed the reflex and responded
|
|
213
|
-
- `turbo-reflex:missing-frame-id` - fires if the reflex cannot determine the target frame id
|
|
214
|
-
- `turbo-reflex:missing-frame` - fires if the the reflex cannot locate the frame element
|
|
215
|
-
- `turbo-reflex:missing-frame-src` - fires if the reflex cannot determine the frame's `src`
|
|
216
211
|
- `turbo-reflex:error` - fires if an unexpected error occurs
|
|
217
212
|
|
|
218
213
|
### Targeting Frames
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var S={frameAttribute:"data-turbo-frame",reflexAttribute:"data-turbo-reflex"},f={...S};var b={};function j(e){b[e.id]=e}function D(e){delete b[e]}var v={add:j,remove:D,get reflexes(){return[...Object.values(b)]},get length(){return Object.keys(b).length}};var c={start:"turbo-reflex:start",success:"turbo-reflex:success",finish:"turbo-reflex:finish",abort:"turbo-reflex:abort",clientError:"turbo-reflex:client-error",serverError:"turbo-reflex:server-error"};function p(e,t=document,r={},n=!1){try{t=t||document;let s=new CustomEvent(e,{detail:r,cancelable:!1,bubbles:!0});t.dispatchEvent(s)}catch(s){if(n)throw s;p(c.clientError,t,{error:s,...r},!0)}}function H(e={}){p(lifecycle.events.clientError,window,e,!0)}function T(e){e.detail.endedAt=new Date().getTime(),e.detail.milliseconds=e.detail.endedAt-e.detail.startedAt,setTimeout(()=>p(c.finish,e.target,e.detail),10)}addEventListener(c.serverError,T);addEventListener(c.success,T);addEventListener(c.finish,e=>v.remove(e.detail.id),!0);var o={dispatch:p,dispatchClientError:H,events:c};function _(e){return e.closest(`[${f.reflexAttribute}]`)}function q(e){return e.closest("turbo-frame")}function P(e,t={}){if(e.tagName.toLowerCase()!=="select")return t.value=e.value;if(!e.multiple)return t.value=e.options[e.selectedIndex].value;t.values=Array.from(e.options).reduce((r,n)=>(n.selected&&r.push(n.value),r),[])}function V(e){let n=Array.from(e.attributes).reduce((s,u)=>{let d=u.value;return typeof d=="string"&&d.length>100&&(d=d.slice(0,100)+"..."),s[u.name]=d,s},{});return n.tag=e.tagName,n.checked=e.checked,n.disabled=e.disabled,P(e,n),typeof n.value=="string"&&n.value.length>500&&(n.value=n.value.slice(0,500)+"..."),delete n.class,delete n[f.reflexAttribute],delete n[f.frameAttribute],n}var i={buildAttributePayload:V,findClosestReflex:_,findClosestFrame:q,get metaElement(){return document.getElementById("turbo-reflex")},get metaElementToken(){return document.getElementById("turbo-reflex").getAttribute("content")}};function X(e){let t="<html",r="</html",n=e.indexOf(t),s=e.lastIndexOf(r);if(n>=0&&s>=0){let u=e.slice(e.indexOf(">",n)+1,s);document.documentElement.innerHTML=u}}function $(e){let t="<turbo-stream",r="</turbo-stream>",n=e.indexOf(t),s=e.lastIndexOf(r);if(n>=0&&s>=0){let u=e.slice(n,s+r.length);document.body.insertAdjacentHTML("beforeend",u)}}var l={renderDocument:X,renderStreams:$};var h={};addEventListener("turbo:before-fetch-request",e=>{let t=e.target.closest("turbo-frame"),{fetchOptions:r}=e.detail;r.headers["TurboReflex-Token"]=i.metaElementToken});addEventListener("turbo:before-fetch-response",e=>{let t=e.target.closest("turbo-frame"),{fetchResponse:r}=e.detail;if(t&&(h[t.id]=t.src,r.header("TurboReflex")==="true")){if(r.statusCode<200||r.statusCode>299){let n=`Server returned a ${r.statusCode} status code! TurboReflex requires 2XX status codes.`;o.dispatchClientError({...e.detail,error:n})}r.header("TurboReflex-Hijacked")==="true"&&(e.preventDefault(),r.responseText.then(n=>l.renderStreams(n)))}});addEventListener("turbo:frame-load",e=>{let t=e.target.closest("turbo-frame");t.dataset.turboReflexSrc=h[t.id]||t.src||t.dataset.turboReflexSrc,delete h[t.id]});var m={},L;function N(e,t){m[e]=t,document.addEventListener(e,L,!0)}function B(e,t){return t=t.toLowerCase(),m[e].includes(t)||!Object.values(m).flat().includes(t)&&m[e].includes("*")}var a={events:m,register:N,isRegistered:B,set handler(e){L=e}};function F(e,t={}){t.token=i.metaElementToken;let r=document.createElement("input");r.type="hidden",r.name="turbo_reflex",r.value=JSON.stringify(t),e.appendChild(r)}var w={invokeReflex:F};function M(e,t={}){let r=document.createElement("a");r.href=e;let n=new URL(r);return n.searchParams.set("turbo_reflex",JSON.stringify(t)),n}var g={build:M};function U(e,t){let r=t.src;t={...t},delete t.src,e.src=g.build(r,t)}var E={invokeReflex:U};function J(e){let t=e.target;o.dispatch(o.events.abort,window,{xhr:t,...e.detail})}function R(e){let t=e.target;t.getResponseHeader("TurboReflex-Hijacked")==="true"?l.renderStreams(t.responseText):l.renderDocument(t.responseText);let r=`Server returned a ${t.status} status code! TurboReflex requires 2XX status codes.`;o.dispatchClientError({xhr:t,...e.detail,error:r})}function G(e){let t=e.target;if(t.status<200||t.status>299)return R(e);let r=t.responseText;t.getResponseHeader("TurboReflex-Hijacked")==="true"?l.renderStreams(t.responseText):l.renderDocument(t.responseText)}function z(e){let t=e.src;e={...e},delete e.src;try{let r=new XMLHttpRequest;r.open("GET",g.build(t,e),!0),r.setRequestHeader("TurboReflex-Token",i.metaElementToken),r.addEventListener("abort",J),r.addEventListener("error",R),r.addEventListener("load",G),r.send()}catch(r){let n=`Unexpected error sending HTTP request! ${r.message}`;R(r,{detail:{message:n}})}}var y={invokeReflex:z};function k(e,t){return t=t||{dataset:{}},e.href||t.src||t.dataset.turboReflexSrc||location.href}function K(e){let t=i.findClosestFrame(e),r=e.dataset.turboFrame;return e.tagName.toLowerCase()==="form"?{name:"form",reason:"Element is a form.",frame:t,src:e.action,invokeReflex:w.invokeReflex}:r&&r!=="_self"?(t=document.getElementById(r),{name:"frame",reason:"element targets a frame that is not _self",frame:t,src:k(e,t),invokeReflex:E.invokeReflex}):(!r||r==="_self")&&t?{name:"frame",reason:"element does NOT target a frame or targets _self and is contained by a frame",frame:t,src:k(e,t),invokeReflex:E.invokeReflex}:{name:"window",reason:"element matches one or more of the following conditions (targets _top, does NOT target a frame, is NOT contained by a frame)",frame:null,src:k(e),invokeReflex:y.invokeReflex}}var A={find:K};var x="unknown",O={debug:Object.values(o.events),info:Object.values(o.events),warn:[o.events.abort,o.events.clientError,o.events.serverError],error:[o.events.clientError,o.events.serverError],unknown:[]};Object.values(o.events).forEach(e=>{addEventListener(e,t=>{O[x].includes(t.type)&&console[x==="debug"?"log":x](t.type,t.detail)})});var C={get level(){return x},set level(e){return Object.keys(O).includes(e)||(e="unknown"),x=e}};function Q(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,e=>(e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16))}var I={v4:Q};function W(e){let t,r={};try{if(t=i.findClosestReflex(e.target),!t||!a.isRegistered(e.type,t.tagName))return;let n=A.find(t);switch(r={id:`reflex-${I.v4()}`,name:t.dataset.turboReflex,driver:n.name,src:n.src,frameId:n.frame?n.frame.id:null,elementId:t.id.length>0?t.id:null,elementAttributes:i.buildAttributePayload(t),startedAt:new Date().getTime()},v.add(r),o.dispatch(o.events.start,t,r),n.name!=="form"&&e.preventDefault(),n.name){case"form":return n.invokeReflex(t,r);case"frame":return n.invokeReflex(n.frame,r);case"window":return n.invokeReflex(r)}}catch(n){o.dispatch(o.events.clientError,t,{error:n,...r})}}a.handler=W;a.register("change",["input","select","textarea"]);a.register("submit",["form"]);a.register("click",["*"]);var Be={schema:f,logger:C,registerEventDelegate:a.register,get eventDelegates(){return{...a.events}},get lifecycleEvents(){return[...Object.values(o.events)]}};export{Be as default};
|
|
2
2
|
//# sourceMappingURL=turbo_reflex.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../javascript/
|
|
4
|
-
"sourcesContent": ["const events = {\n beforeStart: 'turbo-reflex:before-start',\n start: 'turbo-reflex:start',\n finish: 'turbo-reflex:finish',\n error: 'turbo-reflex:error',\n missingFrameId: 'turbo-reflex:missing-frame-id',\n missingFrame: 'turbo-reflex:missing-frame',\n missingFrameSrc: 'turbo-reflex:missing-frame-src'\n}\n\nfunction dispatch (name, target = document, detail = {}) {\n const event = new CustomEvent(name, {\n detail,\n cancelable: true,\n bubbles: true\n })\n target.dispatchEvent(event)\n}\n\nfunction logEventNames () {\n Object.values(events).forEach(name => console.log(name))\n}\n\nexport default { ...events, dispatch, logEventNames }\n", "import LifecycleEvents from './lifecycle_events'\nconst frameSources = {}\n\n// fires after receiving a turbo HTTP response\naddEventListener('turbo:before-fetch-response', event => {\n const frame = event.target\n frameSources[frame.id] = frame.src\n\n const { turboReflexActive, turboReflexElementId } = frame.dataset\n if (!turboReflexActive) return\n\n const element = document.getElementById(turboReflexElementId)\n delete frame.dataset.turboReflexActive\n delete frame.dataset.turboReflexElementId\n\n LifecycleEvents.dispatch(LifecycleEvents.finish, element || document, {\n frame,\n element: element || 'Unknown! Missing id attribute.'\n })\n})\n\n// fires when a frame element is navigated and finishes loading\naddEventListener('turbo:frame-load', event => {\n const frame = event.target\n frame.dataset.turboReflexSrc =\n frameSources[frame.id] || frame.src || frame.dataset.turboReflexSrc\n delete frameSources[frame.id]\n})\n", "const Security = {\n get token () {\n return document.getElementById('turbo-reflex-token').getAttribute('content')\n }\n}\n\nexport default Security\n", "import LifecycleEvents from './lifecycle_events'\n\nfunction findClosestReflex (element) {\n return element.closest('[data-turbo-reflex]')\n}\n\nfunction findClosestFrame (element) {\n return element.closest('turbo-frame')\n}\n\nfunction findFrameId (element) {\n let id = element.dataset.turboFrame\n if (!id) {\n const frame = findClosestFrame(element)\n if (frame) id = frame.id\n }\n if (!id) {\n console.error(\n `The reflex element does not specify a frame!`,\n `Please move the reflex element inside a <turbo-frame> or set the 'data-turbo-frame' attribute.`,\n element\n )\n LifecycleEvents.dispatch(LifecycleEvents.missingFrameId, element, {\n element\n })\n }\n return id\n}\n\nfunction findFrame (id) {\n const frame = document.getElementById(id)\n if (!frame) {\n console.error(`The frame '${id}' does not exist!`)\n LifecycleEvents.dispatch(LifecycleEvents.missingFrame, document, { id })\n }\n return frame\n}\n\nfunction findFrameSrc (frame) {\n const frameSrc = frame.dataset.turboReflexSrc || frame.src\n if (!frameSrc) {\n console.error(\n `The the 'src' for <turbo-frame id='${frame.id}'> is unknown!`,\n `TurboReflex uses 'src' to (re)render frame content after the reflex is invoked.`,\n `Please set the 'src' or 'data-turbo-reflex-src' attribute on the <turbo-frame> element.`,\n frame\n )\n LifecycleEvents.dispatch(LifecycleEvents.missingFrameSrc, frame, { frame })\n }\n return frameSrc\n}\n\nfunction assignElementValueToPayload (element, payload = {}) {\n if (element.tagName.toLowerCase() !== 'select')\n return (payload.value = element.value)\n\n if (!element.multiple)\n return (payload.value = element.options[element.selectedIndex].value)\n\n payload.values = Array.from(element.options).reduce((memo, option) => {\n if (option.selected) memo.push(option.value)\n return memo\n }, [])\n}\n\nfunction buildAttributePayload (element) {\n const payload = Array.from(element.attributes).reduce((memo, attr) => {\n memo[attr.name] = attr.value\n return memo\n }, {})\n\n payload.tag = element.tagName\n payload.checked = element.checked\n payload.disabled = element.disabled\n assignElementValueToPayload(element, payload)\n\n return payload\n}\n\nexport {\n findClosestReflex,\n findClosestFrame,\n findFrameId,\n findFrame,\n findFrameSrc,\n buildAttributePayload\n}\n", "const registeredEvents = {}\nlet eventListener\n\nfunction registerEventListener (fn) {\n eventListener = fn\n}\n\nfunction registerEvent (eventName, tagNames) {\n registeredEvents[eventName] = tagNames\n document.addEventListener(eventName, eventListener, true)\n}\n\nfunction isRegisteredEvent (eventName, tagName) {\n tagName = tagName.toLowerCase()\n return (\n registeredEvents[eventName].includes(tagName) ||\n (!Object.values(registeredEvents)\n .flat()\n .includes(tagName) &&\n registeredEvents[eventName].includes('*'))\n )\n}\n\nfunction logRegisteredEvents () {\n console.log(registeredEvents)\n}\n\nexport {\n registerEventListener,\n registerEvent,\n registeredEvents,\n isRegisteredEvent,\n logRegisteredEvents\n}\n", "import './frame_sources'\nimport Security from './security'\nimport LifecycleEvents from './lifecycle_events'\nimport {\n findClosestReflex,\n findClosestFrame,\n findFrameId,\n findFrame,\n findFrameSrc,\n buildAttributePayload\n} from './elements'\nimport {\n registerEventListener,\n registerEvent,\n registeredEvents,\n isRegisteredEvent,\n logRegisteredEvents\n} from './event_registry'\n\n// fires before making a turbo HTTP request\naddEventListener('turbo:before-fetch-request', event => {\n const frame = event.target\n const { turboReflexActive } = frame.dataset\n if (!turboReflexActive) return\n const { fetchOptions } = event.detail\n fetchOptions.headers['Turbo-Reflex'] = Security.token\n})\n\nfunction buildURL (urlString) {\n const a = document.createElement('a')\n a.href = urlString\n return new URL(a)\n}\n\nfunction invokeFormReflex (form, payload = {}) {\n payload.token = Security.token\n const input = document.createElement('input')\n input.type = 'hidden'\n input.name = 'turbo_reflex'\n input.value = JSON.stringify(payload)\n form.appendChild(input)\n}\n\nfunction invokeReflex (event) {\n let element, frameId, frame, frameSrc\n try {\n element = findClosestReflex(event.target)\n if (!element) return\n\n if (!isRegisteredEvent(event.type, element.tagName)) return\n\n LifecycleEvents.dispatch(LifecycleEvents.beforeStart, element, { element })\n\n frameId = findFrameId(element)\n if (!frameId) return\n\n frame = findFrame(frameId)\n if (!frame) return\n\n frameSrc = findFrameSrc(frame)\n if (!frameSrc) return\n\n const payload = {\n frameId: frameId,\n element: buildAttributePayload(element)\n }\n\n LifecycleEvents.dispatch(LifecycleEvents.start, element, {\n element,\n frameId,\n frame,\n frameSrc,\n payload\n })\n frame.dataset.turboReflexActive = true\n frame.dataset.turboReflexElementId = element.id\n\n if (element.tagName.toLowerCase() === 'form')\n return invokeFormReflex(element, payload)\n\n event.preventDefault()\n const frameURL = buildURL(frameSrc)\n frameURL.searchParams.set('turbo_reflex', JSON.stringify(payload))\n frame.src = frameURL.toString()\n } catch (error) {\n console.error(\n `TurboReflex encountered an unexpected error!`,\n { element, frameId, frame, frameSrc, target: event.target },\n error\n )\n LifecycleEvents.dispatch(LifecycleEvents.error, element || document, {\n element,\n frameId,\n frame,\n frameSrc,\n error\n })\n }\n}\n\n// wire things up and setup default events\nregisterEventListener(invokeReflex)\nregisterEvent('change', ['input', 'select', 'textarea'])\nregisterEvent('submit', ['form'])\nregisterEvent('click', ['*'])\n\nexport default {\n registerEvent,\n logRegisteredEvents,\n logLifecycleEventNames: LifecycleEvents.logEventNames\n}\n"],
|
|
5
|
-
"mappings": "AAAA,IAAMA,EAAS,CACb,
|
|
6
|
-
"names": ["events", "dispatch", "name", "target", "detail", "
|
|
3
|
+
"sources": ["../../javascript/schema.js", "../../javascript/activity.js", "../../javascript/lifecycle.js", "../../javascript/elements.js", "../../javascript/renderer.js", "../../javascript/turbo.js", "../../javascript/delegates.js", "../../javascript/drivers/form.js", "../../javascript/urls.js", "../../javascript/drivers/frame.js", "../../javascript/drivers/window.js", "../../javascript/drivers/index.js", "../../javascript/logger.js", "../../javascript/uuids.js", "../../javascript/index.js"],
|
|
4
|
+
"sourcesContent": ["const schema = {\n frameAttribute: 'data-turbo-frame',\n reflexAttribute: 'data-turbo-reflex'\n}\n\nexport default { ...schema }\n", "const active = {}\n\nfunction add (payload) {\n active[payload.id] = payload\n}\n\nfunction remove (id) {\n delete active[id]\n}\n\nexport default {\n add,\n remove,\n get reflexes () {\n return [...Object.values(active)]\n },\n get length () {\n return Object.keys(active).length\n }\n}\n", "import activity from './activity'\n\nconst events = {\n start: 'turbo-reflex:start',\n success: 'turbo-reflex:success',\n finish: 'turbo-reflex:finish',\n abort: 'turbo-reflex:abort',\n clientError: 'turbo-reflex:client-error',\n serverError: 'turbo-reflex:server-error'\n}\n\nfunction dispatch (name, target = document, detail = {}, raise = false) {\n try {\n target = target || document\n const event = new CustomEvent(name, {\n detail,\n cancelable: false,\n bubbles: true\n })\n target.dispatchEvent(event)\n } catch (error) {\n if (raise) throw error\n dispatch(events.clientError, target, { error, ...detail }, true)\n }\n}\n\nfunction dispatchClientError (detail = {}) {\n dispatch(lifecycle.events.clientError, window, detail, true)\n}\n\nfunction finish (event) {\n event.detail.endedAt = new Date().getTime()\n event.detail.milliseconds = event.detail.endedAt - event.detail.startedAt\n setTimeout(() => dispatch(events.finish, event.target, event.detail), 10)\n}\n\naddEventListener(events.serverError, finish)\naddEventListener(events.success, finish)\naddEventListener(events.finish, event => activity.remove(event.detail.id), true)\n\nexport default {\n dispatch,\n dispatchClientError,\n events\n}\n", "import schema from './schema.js'\nimport lifecycle from './lifecycle'\n\nfunction findClosestReflex (element) {\n return element.closest(`[${schema.reflexAttribute}]`)\n}\n\nfunction findClosestFrame (element) {\n return element.closest('turbo-frame')\n}\n\nfunction assignElementValueToPayload (element, payload = {}) {\n if (element.tagName.toLowerCase() !== 'select')\n return (payload.value = element.value)\n\n if (!element.multiple)\n return (payload.value = element.options[element.selectedIndex].value)\n\n payload.values = Array.from(element.options).reduce((memo, option) => {\n if (option.selected) memo.push(option.value)\n return memo\n }, [])\n}\n\nfunction buildAttributePayload (element) {\n // truncate long values to optimize payload size\n // TODO: revisit this decision\n const maxAttributeLength = 100\n const maxValueLength = 500\n\n const payload = Array.from(element.attributes).reduce((memo, attr) => {\n let value = attr.value\n if (typeof value === 'string' && value.length > maxAttributeLength)\n value = value.slice(0, maxAttributeLength) + '...'\n memo[attr.name] = value\n return memo\n }, {})\n\n payload.tag = element.tagName\n payload.checked = element.checked\n payload.disabled = element.disabled\n assignElementValueToPayload(element, payload)\n\n if (\n typeof payload.value === 'string' &&\n payload.value.length > maxValueLength\n )\n payload.value = payload.value.slice(0, maxValueLength) + '...'\n\n delete payload.class\n delete payload[schema.reflexAttribute]\n delete payload[schema.frameAttribute]\n return payload\n}\n\nexport default {\n buildAttributePayload,\n findClosestReflex,\n findClosestFrame,\n get metaElement () {\n return document.getElementById('turbo-reflex')\n },\n get metaElementToken () {\n return document.getElementById('turbo-reflex').getAttribute('content')\n }\n}\n", "function renderDocument (content) {\n const head = '<html'\n const tail = '</html'\n const headIndex = content.indexOf(head)\n const tailIndex = content.lastIndexOf(tail)\n if (headIndex >= 0 && tailIndex >= 0) {\n const html = content.slice(content.indexOf('>', headIndex) + 1, tailIndex)\n document.documentElement.innerHTML = html\n }\n}\n\nfunction renderStreams (content) {\n const head = '<turbo-stream'\n const tail = '</turbo-stream>'\n const headIndex = content.indexOf(head)\n const tailIndex = content.lastIndexOf(tail)\n if (headIndex >= 0 && tailIndex >= 0) {\n const streams = content.slice(headIndex, tailIndex + tail.length)\n document.body.insertAdjacentHTML('beforeend', streams)\n }\n}\n\nexport default { renderDocument, renderStreams }\n", "import elements from './elements'\nimport renderer from './renderer'\nimport lifecycle from './lifecycle'\n\nconst frameSources = {}\n\n// fires before making a turbo HTTP request\naddEventListener('turbo:before-fetch-request', event => {\n const frame = event.target.closest('turbo-frame')\n const { fetchOptions } = event.detail\n fetchOptions.headers['TurboReflex-Token'] = elements.metaElementToken\n})\n\n// fires after receiving a turbo HTTP response\naddEventListener('turbo:before-fetch-response', event => {\n const frame = event.target.closest('turbo-frame')\n const { fetchResponse: response } = event.detail\n\n if (frame) {\n frameSources[frame.id] = frame.src\n\n if (response.header('TurboReflex') === 'true') {\n if (response.statusCode < 200 || response.statusCode > 299) {\n const error = `Server returned a ${response.statusCode} status code! TurboReflex requires 2XX status codes.`\n lifecycle.dispatchClientError({ ...event.detail, error })\n }\n\n if (response.header('TurboReflex-Hijacked') === 'true') {\n event.preventDefault()\n response.responseText.then(content => renderer.renderStreams(content))\n }\n }\n }\n})\n\n// fires when a frame element is navigated and finishes loading\naddEventListener('turbo:frame-load', event => {\n const frame = event.target.closest('turbo-frame')\n frame.dataset.turboReflexSrc =\n frameSources[frame.id] || frame.src || frame.dataset.turboReflexSrc\n delete frameSources[frame.id]\n})\n", "const events = {}\nlet eventListener\n\nfunction register (eventName, tagNames) {\n events[eventName] = tagNames\n document.addEventListener(eventName, eventListener, true)\n}\n\nfunction isRegistered (eventName, tagName) {\n tagName = tagName.toLowerCase()\n return (\n events[eventName].includes(tagName) ||\n (!Object.values(events)\n .flat()\n .includes(tagName) &&\n events[eventName].includes('*'))\n )\n}\n\nexport default {\n events,\n register,\n isRegistered,\n set handler (fn) {\n eventListener = fn\n }\n}\n", "import elements from '../elements'\n\nfunction invokeReflex (form, payload = {}) {\n payload.token = elements.metaElementToken\n const input = document.createElement('input')\n input.type = 'hidden'\n input.name = 'turbo_reflex'\n input.value = JSON.stringify(payload)\n form.appendChild(input)\n}\n\nexport default { invokeReflex }\n", "function build (urlString, payload = {}) {\n const a = document.createElement('a')\n a.href = urlString\n const url = new URL(a)\n url.searchParams.set('turbo_reflex', JSON.stringify(payload))\n return url\n}\n\nexport default { build }\n", "import urls from '../urls'\nimport elements from '../elements'\n\nfunction invokeReflex (frame, payload) {\n const src = payload.src\n payload = { ...payload }\n delete payload.src\n frame.src = urls.build(src, payload)\n}\n\nexport default { invokeReflex }\n", "import elements from '../elements'\nimport lifecycle from '../lifecycle'\nimport urls from '../urls'\nimport renderer from '../renderer'\n\nfunction aborted (event) {\n const xhr = event.target\n lifecycle.dispatch(lifecycle.events.abort, window, { xhr, ...event.detail })\n}\n\nfunction errored (event) {\n const xhr = event.target\n\n xhr.getResponseHeader('TurboReflex-Hijacked') === 'true'\n ? renderer.renderStreams(xhr.responseText)\n : renderer.renderDocument(xhr.responseText)\n\n const error = `Server returned a ${xhr.status} status code! TurboReflex requires 2XX status codes.`\n lifecycle.dispatchClientError({ xhr, ...event.detail, error })\n}\n\nfunction loaded (event) {\n const xhr = event.target\n if (xhr.status < 200 || xhr.status > 299) return errored(event)\n const content = xhr.responseText\n xhr.getResponseHeader('TurboReflex-Hijacked') === 'true'\n ? renderer.renderStreams(xhr.responseText)\n : renderer.renderDocument(xhr.responseText)\n}\n\nfunction invokeReflex (payload) {\n const src = payload.src\n payload = { ...payload }\n delete payload.src\n\n try {\n const xhr = new XMLHttpRequest()\n xhr.open('GET', urls.build(src, payload), true)\n xhr.setRequestHeader('TurboReflex-Token', elements.metaElementToken)\n xhr.addEventListener('abort', aborted)\n xhr.addEventListener('error', errored)\n xhr.addEventListener('load', loaded)\n xhr.send()\n } catch (ex) {\n const message = `Unexpected error sending HTTP request! ${ex.message}`\n errored(ex, { detail: { message } })\n }\n}\n\nexport default { invokeReflex }\n", "import elements from '../elements'\nimport formDriver from './form'\nimport frameDriver from './frame'\nimport windowDriver from './window'\n\nfunction src (element, frame) {\n frame = frame || { dataset: {} }\n return (\n element.href || frame.src || frame.dataset.turboReflexSrc || location.href\n )\n}\n\nfunction find (element) {\n let frame = elements.findClosestFrame(element)\n const targetId = element.dataset.turboFrame\n\n if (element.tagName.toLowerCase() === 'form')\n return {\n name: 'form',\n reason: 'Element is a form.',\n frame,\n src: element.action,\n invokeReflex: formDriver.invokeReflex\n }\n\n // element targets a frame that is not _self\n if (targetId && targetId !== '_self') {\n frame = document.getElementById(targetId)\n return {\n name: 'frame',\n reason: 'element targets a frame that is not _self',\n frame,\n src: src(element, frame),\n invokeReflex: frameDriver.invokeReflex\n }\n }\n\n // element does NOT target a frame or targets _self and is contained by a frame\n if ((!targetId || targetId === '_self') && frame)\n return {\n name: 'frame',\n reason:\n 'element does NOT target a frame or targets _self and is contained by a frame',\n frame,\n src: src(element, frame),\n invokeReflex: frameDriver.invokeReflex\n }\n\n // element matches one or more of the following conditions\n // - targets _top\n // - does NOT target a frame\n // - is NOT contained by a frame\n return {\n name: 'window',\n reason:\n 'element matches one or more of the following conditions (targets _top, does NOT target a frame, is NOT contained by a frame)',\n frame: null,\n src: src(element),\n invokeReflex: windowDriver.invokeReflex\n }\n}\n\nexport default { find }\n", "import lifecycle from './lifecycle'\n\nlet currentLevel = 'unknown'\n\nconst logLevels = {\n debug: Object.values(lifecycle.events),\n info: Object.values(lifecycle.events),\n warn: [\n lifecycle.events.abort,\n lifecycle.events.clientError,\n lifecycle.events.serverError\n ],\n error: [lifecycle.events.clientError, lifecycle.events.serverError],\n unknown: []\n}\n\nObject.values(lifecycle.events).forEach(name => {\n addEventListener(name, event => {\n if (logLevels[currentLevel].includes(event.type)) {\n const level = currentLevel === 'debug' ? 'log' : currentLevel\n console[level](event.type, event.detail)\n }\n })\n})\n\nexport default {\n get level () {\n return currentLevel\n },\n set level (value) {\n if (!Object.keys(logLevels).includes(value)) value = 'unknown'\n return (currentLevel = value)\n }\n}\n", "function v4 () {\n return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>\n (\n c ^\n (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))\n ).toString(16)\n )\n}\n\nexport default { v4 }\n", "import './turbo'\nimport schema from './schema.js'\nimport activity from './activity'\nimport delegates from './delegates'\nimport drivers from './drivers'\nimport elements from './elements'\nimport lifecycle from './lifecycle'\nimport logger from './logger'\nimport urls from './urls'\nimport uuids from './uuids'\n\nfunction invokeReflex (event) {\n let element\n let payload = {}\n\n try {\n element = elements.findClosestReflex(event.target)\n if (!element) return\n if (!delegates.isRegistered(event.type, element.tagName)) return\n\n const driver = drivers.find(element)\n\n // payload sent to server (also used for lifecycle event.detail)\n payload = {\n id: `reflex-${uuids.v4()}`,\n name: element.dataset.turboReflex,\n driver: driver.name,\n src: driver.src,\n frameId: driver.frame ? driver.frame.id : null,\n elementId: element.id.length > 0 ? element.id : null,\n elementAttributes: elements.buildAttributePayload(element),\n startedAt: new Date().getTime()\n }\n\n activity.add(payload)\n lifecycle.dispatch(lifecycle.events.start, element, payload)\n\n if (driver.name !== 'form') event.preventDefault()\n\n switch (driver.name) {\n case 'form':\n return driver.invokeReflex(element, payload)\n case 'frame':\n return driver.invokeReflex(driver.frame, payload)\n case 'window':\n return driver.invokeReflex(payload)\n }\n } catch (error) {\n lifecycle.dispatch(lifecycle.events.clientError, element, {\n error,\n ...payload\n })\n }\n}\n\n// wire things up and setup defaults for event delegation\ndelegates.handler = invokeReflex\ndelegates.register('change', ['input', 'select', 'textarea'])\ndelegates.register('submit', ['form'])\ndelegates.register('click', ['*'])\n\nexport default {\n schema,\n logger,\n registerEventDelegate: delegates.register,\n get eventDelegates () {\n return { ...delegates.events }\n },\n get lifecycleEvents () {\n return [...Object.values(lifecycle.events)]\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["schema", "schema_default", "active", "add", "payload", "remove", "id", "activity_default", "events", "dispatch", "name", "target", "detail", "raise", "event", "error", "dispatchClientError", "finish", "activity_default", "lifecycle_default", "findClosestReflex", "element", "schema_default", "findClosestFrame", "assignElementValueToPayload", "payload", "memo", "option", "buildAttributePayload", "attr", "value", "elements_default", "renderDocument", "content", "head", "tail", "headIndex", "tailIndex", "html", "renderStreams", "streams", "renderer_default", "frameSources", "event", "frame", "fetchOptions", "elements_default", "response", "error", "lifecycle_default", "content", "renderer_default", "events", "eventListener", "register", "eventName", "tagNames", "isRegistered", "tagName", "delegates_default", "fn", "invokeReflex", "form", "payload", "elements_default", "input", "form_default", "build", "urlString", "payload", "a", "url", "urls_default", "invokeReflex", "frame", "payload", "src", "urls_default", "frame_default", "aborted", "event", "xhr", "lifecycle_default", "errored", "renderer_default", "error", "loaded", "content", "invokeReflex", "payload", "src", "urls_default", "elements_default", "ex", "message", "window_default", "src", "element", "frame", "find", "elements_default", "targetId", "form_default", "frame_default", "window_default", "drivers_default", "currentLevel", "logLevels", "lifecycle_default", "name", "event", "logger_default", "value", "v4", "c", "uuids_default", "invokeReflex", "event", "element", "payload", "elements_default", "delegates_default", "driver", "drivers_default", "uuids_default", "activity_default", "lifecycle_default", "error", "javascript_default", "schema_default", "logger_default"]
|
|
7
7
|
}
|
|
@@ -4,130 +4,34 @@ module TurboReflex::Controller
|
|
|
4
4
|
extend ActiveSupport::Concern
|
|
5
5
|
|
|
6
6
|
included do
|
|
7
|
-
before_action :
|
|
8
|
-
after_action :
|
|
9
|
-
after_action :assign_turbo_reflex_token
|
|
7
|
+
before_action :run_turbo_reflex
|
|
8
|
+
after_action :append_turbo_reflex_to_response
|
|
10
9
|
helper_method :turbo_reflex_meta_tag, :turbo_reflex_performed?, :turbo_reflex_requested?
|
|
11
|
-
# helper TurboReflex::TurboReflexHelper # only required if we isolate_namespace
|
|
12
10
|
end
|
|
13
11
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
options = {id: "turbo-reflex-token", name: "turbo-reflex-token", content: masked_token}
|
|
17
|
-
view_context.tag("meta", options).html_safe
|
|
12
|
+
def turbo_reflex_runner
|
|
13
|
+
@turbo_reflex_runner ||= TurboReflex::Runner.new(self)
|
|
18
14
|
end
|
|
19
15
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
@turbo_reflex_params ||= begin
|
|
23
|
-
payload = JSON.parse(params[:turbo_reflex]).deep_transform_keys(&:underscore)
|
|
24
|
-
ActionController::Parameters.new(payload).permit!
|
|
25
|
-
end
|
|
16
|
+
def turbo_reflex_meta_tag
|
|
17
|
+
turbo_reflex_runner.meta_tag
|
|
26
18
|
end
|
|
27
19
|
|
|
28
20
|
def turbo_reflex_requested?
|
|
29
|
-
|
|
30
|
-
return false unless turbo_reflex_params.present?
|
|
31
|
-
true
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def turbo_reflex_element
|
|
35
|
-
return nil if turbo_reflex_params.blank?
|
|
36
|
-
@turbo_reflex_element ||= Struct
|
|
37
|
-
.new(*turbo_reflex_params[:element].keys.map { |key| key.to_s.parameterize.underscore.to_sym })
|
|
38
|
-
.new(*turbo_reflex_params[:element].values)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def turbo_reflex_name
|
|
42
|
-
return nil unless turbo_reflex_requested?
|
|
43
|
-
turbo_reflex_element.data_turbo_reflex
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def turbo_reflex_class_name
|
|
47
|
-
return nil unless turbo_reflex_requested?
|
|
48
|
-
turbo_reflex_name.split("#").first
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def turbo_reflex_method_name
|
|
52
|
-
return nil unless turbo_reflex_requested?
|
|
53
|
-
turbo_reflex_name.split("#").last
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def turbo_reflex_class
|
|
57
|
-
@turbo_reflex_class ||= turbo_reflex_class_name&.safe_constantize
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def turbo_reflex_instance
|
|
61
|
-
@turbo_reflex_instance ||= turbo_reflex_class&.new(self)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def turbo_reflex_valid?
|
|
65
|
-
return false if request.get? && client_turbo_reflex_token.blank?
|
|
66
|
-
return false unless valid_turbo_reflex_token?
|
|
67
|
-
return false unless turbo_reflex_instance.is_a?(TurboReflex::Base)
|
|
68
|
-
turbo_reflex_instance.respond_to? turbo_reflex_method_name
|
|
21
|
+
turbo_reflex_runner.reflex_requested?
|
|
69
22
|
end
|
|
70
23
|
|
|
71
24
|
def turbo_reflex_performed?
|
|
72
|
-
|
|
25
|
+
turbo_reflex_runner.reflex_performed?
|
|
73
26
|
end
|
|
74
27
|
|
|
75
28
|
protected
|
|
76
29
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
turbo_reflex_instance.public_send turbo_reflex_method_name
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def append_turbo_reflex_turbo_streams
|
|
83
|
-
return unless turbo_reflex_performed?
|
|
84
|
-
return unless turbo_reflex_instance&.turbo_streams.present?
|
|
85
|
-
append_turbo_reflex_content turbo_reflex_instance.turbo_streams.map(&:to_s).join
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
def turbo_reflex_message_verifier
|
|
91
|
-
ActiveSupport::MessageVerifier.new session.id.to_s, digest: "SHA256"
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def client_turbo_reflex_token
|
|
95
|
-
(request.headers["Turbo-Reflex"] || turbo_reflex_params[:token]).to_s
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def new_turbo_reflex_token
|
|
99
|
-
@new_turbo_reflex_token ||= SecureRandom.urlsafe_base64(32)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def current_turbo_reflex_token
|
|
103
|
-
session[:turbo_reflex_token]
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def valid_turbo_reflex_token?
|
|
107
|
-
return false unless turbo_reflex_message_verifier.valid_message?(client_turbo_reflex_token)
|
|
108
|
-
unmasked_token = turbo_reflex_message_verifier.verify(client_turbo_reflex_token)
|
|
109
|
-
unmasked_token == current_turbo_reflex_token
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def assign_turbo_reflex_token
|
|
113
|
-
return unless turbo_reflex_requested? || client_turbo_reflex_token.blank?
|
|
114
|
-
session[:turbo_reflex_token] = new_turbo_reflex_token
|
|
115
|
-
append_turbo_reflex_content turbo_stream.replace("turbo-reflex-token", turbo_reflex_meta_tag)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def turbo_reflex_response_type
|
|
119
|
-
body = response.body.to_s.strip
|
|
120
|
-
return :stream if body.ends_with?("</turbo-stream>")
|
|
121
|
-
return :frame if body.ends_with?("</turbo-frame>")
|
|
122
|
-
:default
|
|
30
|
+
def run_turbo_reflex
|
|
31
|
+
turbo_reflex_runner.run
|
|
123
32
|
end
|
|
124
33
|
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
case turbo_reflex_response_type
|
|
128
|
-
when :stream then response.body << sanitized_content
|
|
129
|
-
when :frame then response.body.sub!("</turbo-frame>", "#{sanitized_content}</turbo-frame>")
|
|
130
|
-
when :default then response.body.sub!("</body>", "#{sanitized_content}</body>")
|
|
131
|
-
end
|
|
34
|
+
def append_turbo_reflex_to_response
|
|
35
|
+
turbo_reflex_runner.append_to_response
|
|
132
36
|
end
|
|
133
37
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const active = {}
|
|
2
|
+
|
|
3
|
+
function add (payload) {
|
|
4
|
+
active[payload.id] = payload
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function remove (id) {
|
|
8
|
+
delete active[id]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
add,
|
|
13
|
+
remove,
|
|
14
|
+
get reflexes () {
|
|
15
|
+
return [...Object.values(active)]
|
|
16
|
+
},
|
|
17
|
+
get length () {
|
|
18
|
+
return Object.keys(active).length
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const events = {}
|
|
2
|
+
let eventListener
|
|
3
|
+
|
|
4
|
+
function register (eventName, tagNames) {
|
|
5
|
+
events[eventName] = tagNames
|
|
6
|
+
document.addEventListener(eventName, eventListener, true)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isRegistered (eventName, tagName) {
|
|
10
|
+
tagName = tagName.toLowerCase()
|
|
11
|
+
return (
|
|
12
|
+
events[eventName].includes(tagName) ||
|
|
13
|
+
(!Object.values(events)
|
|
14
|
+
.flat()
|
|
15
|
+
.includes(tagName) &&
|
|
16
|
+
events[eventName].includes('*'))
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
events,
|
|
22
|
+
register,
|
|
23
|
+
isRegistered,
|
|
24
|
+
set handler (fn) {
|
|
25
|
+
eventListener = fn
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import elements from '../elements'
|
|
2
|
+
|
|
3
|
+
function invokeReflex (form, payload = {}) {
|
|
4
|
+
payload.token = elements.metaElementToken
|
|
5
|
+
const input = document.createElement('input')
|
|
6
|
+
input.type = 'hidden'
|
|
7
|
+
input.name = 'turbo_reflex'
|
|
8
|
+
input.value = JSON.stringify(payload)
|
|
9
|
+
form.appendChild(input)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default { invokeReflex }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import urls from '../urls'
|
|
2
|
+
import elements from '../elements'
|
|
3
|
+
|
|
4
|
+
function invokeReflex (frame, payload) {
|
|
5
|
+
const src = payload.src
|
|
6
|
+
payload = { ...payload }
|
|
7
|
+
delete payload.src
|
|
8
|
+
frame.src = urls.build(src, payload)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default { invokeReflex }
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import elements from '../elements'
|
|
2
|
+
import formDriver from './form'
|
|
3
|
+
import frameDriver from './frame'
|
|
4
|
+
import windowDriver from './window'
|
|
5
|
+
|
|
6
|
+
function src (element, frame) {
|
|
7
|
+
frame = frame || { dataset: {} }
|
|
8
|
+
return (
|
|
9
|
+
element.href || frame.src || frame.dataset.turboReflexSrc || location.href
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function find (element) {
|
|
14
|
+
let frame = elements.findClosestFrame(element)
|
|
15
|
+
const targetId = element.dataset.turboFrame
|
|
16
|
+
|
|
17
|
+
if (element.tagName.toLowerCase() === 'form')
|
|
18
|
+
return {
|
|
19
|
+
name: 'form',
|
|
20
|
+
reason: 'Element is a form.',
|
|
21
|
+
frame,
|
|
22
|
+
src: element.action,
|
|
23
|
+
invokeReflex: formDriver.invokeReflex
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// element targets a frame that is not _self
|
|
27
|
+
if (targetId && targetId !== '_self') {
|
|
28
|
+
frame = document.getElementById(targetId)
|
|
29
|
+
return {
|
|
30
|
+
name: 'frame',
|
|
31
|
+
reason: 'element targets a frame that is not _self',
|
|
32
|
+
frame,
|
|
33
|
+
src: src(element, frame),
|
|
34
|
+
invokeReflex: frameDriver.invokeReflex
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// element does NOT target a frame or targets _self and is contained by a frame
|
|
39
|
+
if ((!targetId || targetId === '_self') && frame)
|
|
40
|
+
return {
|
|
41
|
+
name: 'frame',
|
|
42
|
+
reason:
|
|
43
|
+
'element does NOT target a frame or targets _self and is contained by a frame',
|
|
44
|
+
frame,
|
|
45
|
+
src: src(element, frame),
|
|
46
|
+
invokeReflex: frameDriver.invokeReflex
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// element matches one or more of the following conditions
|
|
50
|
+
// - targets _top
|
|
51
|
+
// - does NOT target a frame
|
|
52
|
+
// - is NOT contained by a frame
|
|
53
|
+
return {
|
|
54
|
+
name: 'window',
|
|
55
|
+
reason:
|
|
56
|
+
'element matches one or more of the following conditions (targets _top, does NOT target a frame, is NOT contained by a frame)',
|
|
57
|
+
frame: null,
|
|
58
|
+
src: src(element),
|
|
59
|
+
invokeReflex: windowDriver.invokeReflex
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default { find }
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import elements from '../elements'
|
|
2
|
+
import lifecycle from '../lifecycle'
|
|
3
|
+
import urls from '../urls'
|
|
4
|
+
import renderer from '../renderer'
|
|
5
|
+
|
|
6
|
+
function aborted (event) {
|
|
7
|
+
const xhr = event.target
|
|
8
|
+
lifecycle.dispatch(lifecycle.events.abort, window, { xhr, ...event.detail })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function errored (event) {
|
|
12
|
+
const xhr = event.target
|
|
13
|
+
|
|
14
|
+
xhr.getResponseHeader('TurboReflex-Hijacked') === 'true'
|
|
15
|
+
? renderer.renderStreams(xhr.responseText)
|
|
16
|
+
: renderer.renderDocument(xhr.responseText)
|
|
17
|
+
|
|
18
|
+
const error = `Server returned a ${xhr.status} status code! TurboReflex requires 2XX status codes.`
|
|
19
|
+
lifecycle.dispatchClientError({ xhr, ...event.detail, error })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function loaded (event) {
|
|
23
|
+
const xhr = event.target
|
|
24
|
+
if (xhr.status < 200 || xhr.status > 299) return errored(event)
|
|
25
|
+
const content = xhr.responseText
|
|
26
|
+
xhr.getResponseHeader('TurboReflex-Hijacked') === 'true'
|
|
27
|
+
? renderer.renderStreams(xhr.responseText)
|
|
28
|
+
: renderer.renderDocument(xhr.responseText)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function invokeReflex (payload) {
|
|
32
|
+
const src = payload.src
|
|
33
|
+
payload = { ...payload }
|
|
34
|
+
delete payload.src
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const xhr = new XMLHttpRequest()
|
|
38
|
+
xhr.open('GET', urls.build(src, payload), true)
|
|
39
|
+
xhr.setRequestHeader('TurboReflex-Token', elements.metaElementToken)
|
|
40
|
+
xhr.addEventListener('abort', aborted)
|
|
41
|
+
xhr.addEventListener('error', errored)
|
|
42
|
+
xhr.addEventListener('load', loaded)
|
|
43
|
+
xhr.send()
|
|
44
|
+
} catch (ex) {
|
|
45
|
+
const message = `Unexpected error sending HTTP request! ${ex.message}`
|
|
46
|
+
errored(ex, { detail: { message } })
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default { invokeReflex }
|