turbo_reflex 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +10 -17
  3. data/README.md +1 -6
  4. data/app/assets/builds/turbo_reflex.js +1 -1
  5. data/app/assets/builds/turbo_reflex.js.map +4 -4
  6. data/app/controllers/concerns/turbo_reflex/controller.rb +12 -108
  7. data/app/javascript/activity.js +20 -0
  8. data/app/javascript/delegates.js +27 -0
  9. data/app/javascript/drivers/form.js +12 -0
  10. data/app/javascript/drivers/frame.js +11 -0
  11. data/app/javascript/drivers/index.js +63 -0
  12. data/app/javascript/drivers/window.js +50 -0
  13. data/app/javascript/elements.js +29 -50
  14. data/app/javascript/index.js +72 -0
  15. data/app/javascript/lifecycle.js +45 -0
  16. data/app/javascript/logger.js +34 -0
  17. data/app/javascript/renderer.js +23 -0
  18. data/app/javascript/schema.js +6 -0
  19. data/app/javascript/turbo.js +42 -0
  20. data/app/javascript/urls.js +9 -0
  21. data/app/javascript/uuids.js +10 -0
  22. data/lib/turbo_reflex/base.rb +59 -9
  23. data/lib/turbo_reflex/engine.rb +1 -9
  24. data/lib/turbo_reflex/runner.rb +202 -0
  25. data/lib/turbo_reflex/version.rb +1 -1
  26. data/package.json +5 -4
  27. data/turbo_reflex.gemspec +1 -0
  28. data/yarn.lock +135 -135
  29. metadata +31 -11
  30. data/app/controllers/turbo_reflex/turbo_reflexes_controller.rb +0 -12
  31. data/app/helpers/turbo_reflex/turbo_reflex_helper.rb +0 -9
  32. data/app/javascript/event_registry.js +0 -34
  33. data/app/javascript/frame_sources.js +0 -28
  34. data/app/javascript/lifecycle_events.js +0 -24
  35. data/app/javascript/security.js +0 -7
  36. data/app/javascript/turbo_reflex.js +0 -111
  37. data/config/routes.rb +0 -5
  38. data/lib/turbo_reflex/errors.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 120854ae5417fe826b11a24e87acd266f390444c7d334861e315bf3a955406e3
4
- data.tar.gz: de07d29af7d13096cca47abf76de7604eb758a3246ddd35951db6664b5d17005
3
+ metadata.gz: a31a4a624ebc825d519458818b34735051ab4fefd4d03d8b3c974ec96cae27fd
4
+ data.tar.gz: 5e4df1345eeedcc08ef0ee5989575c38f38a4956495654f4f3416d596dd3c6b0
5
5
  SHA512:
6
- metadata.gz: 9323c7d812807dfcb3a881f26a2ba7e17cd9425440ab12ec9a5e0af335384d6decb4259208c2c5bfcd272182b39ef3478e9088d0d0872e9d4637d86cbd1d53cd
7
- data.tar.gz: c4878c6927fa6c1b5608e91b58f828bd49c5fe1078cd989455d477f232bfb8199df61acc84e2fa0869496f54b804573cfa6667ecdf129ae7064c76a2d3c41c79
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.6)
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.2.3)
133
- digest
132
+ net-imap (0.3.1)
134
133
  net-protocol
135
- strscan
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.1)
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.4.0)
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.0-aarch64-linux)
228
- sqlite3 (1.5.0-arm64-darwin)
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.1.0)
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.0)
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-400-47d299.svg" />
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 l={beforeStart:"turbo-reflex:before-start",start:"turbo-reflex:start",finish:"turbo-reflex:finish",error:"turbo-reflex:error",missingFrameId:"turbo-reflex:missing-frame-id",missingFrame:"turbo-reflex:missing-frame",missingFrameSrc:"turbo-reflex:missing-frame-src"};function y(t,e=document,r={}){let n=new CustomEvent(t,{detail:r,cancelable:!0,bubbles:!0});e.dispatchEvent(n)}function L(){Object.values(l).forEach(t=>console.log(t))}var o={...l,dispatch:y,logEventNames:L};var c={};addEventListener("turbo:before-fetch-response",t=>{let e=t.target;c[e.id]=e.src;let{turboReflexActive:r,turboReflexElementId:n}=e.dataset;if(!r)return;let s=document.getElementById(n);delete e.dataset.turboReflexActive,delete e.dataset.turboReflexElementId,o.dispatch(o.finish,s||document,{frame:e,element:s||"Unknown! Missing id attribute."})});addEventListener("turbo:frame-load",t=>{let e=t.target;e.dataset.turboReflexSrc=c[e.id]||e.src||e.dataset.turboReflexSrc,delete c[e.id]});var S={get token(){return document.getElementById("turbo-reflex-token").getAttribute("content")}},f=S;function m(t){return t.closest("[data-turbo-reflex]")}function F(t){return t.closest("turbo-frame")}function b(t){let e=t.dataset.turboFrame;if(!e){let r=F(t);r&&(e=r.id)}return e||(console.error("The reflex element does not specify a frame!","Please move the reflex element inside a <turbo-frame> or set the 'data-turbo-frame' attribute.",t),o.dispatch(o.missingFrameId,t,{element:t})),e}function g(t){let e=document.getElementById(t);return e||(console.error(`The frame '${t}' does not exist!`),o.dispatch(o.missingFrame,document,{id:t})),e}function x(t){let e=t.dataset.turboReflexSrc||t.src;return e||(console.error(`The the 'src' for <turbo-frame id='${t.id}'> is unknown!`,"TurboReflex uses 'src' to (re)render frame content after the reflex is invoked.","Please set the 'src' or 'data-turbo-reflex-src' attribute on the <turbo-frame> element.",t),o.dispatch(o.missingFrameSrc,t,{frame:t})),e}function k(t,e={}){if(t.tagName.toLowerCase()!=="select")return e.value=t.value;if(!t.multiple)return e.value=t.options[t.selectedIndex].value;e.values=Array.from(t.options).reduce((r,n)=>(n.selected&&r.push(n.value),r),[])}function v(t){let e=Array.from(t.attributes).reduce((r,n)=>(r[n.name]=n.value,r),{});return e.tag=t.tagName,e.checked=t.checked,e.disabled=t.disabled,k(t,e),e}var a={},h;function p(t){h=t}function u(t,e){a[t]=e,document.addEventListener(t,h,!0)}function E(t,e){return e=e.toLowerCase(),a[t].includes(e)||!Object.values(a).flat().includes(e)&&a[t].includes("*")}function R(){console.log(a)}addEventListener("turbo:before-fetch-request",t=>{let e=t.target,{turboReflexActive:r}=e.dataset;if(!r)return;let{fetchOptions:n}=t.detail;n.headers["Turbo-Reflex"]=f.token});function I(t){let e=document.createElement("a");return e.href=t,new URL(e)}function A(t,e={}){e.token=f.token;let r=document.createElement("input");r.type="hidden",r.name="turbo_reflex",r.value=JSON.stringify(e),t.appendChild(r)}function C(t){let e,r,n,s;try{if(e=m(t.target),!e||!E(t.type,e.tagName)||(o.dispatch(o.beforeStart,e,{element:e}),r=b(e),!r)||(n=g(r),!n)||(s=x(n),!s))return;let i={frameId:r,element:v(e)};if(o.dispatch(o.start,e,{element:e,frameId:r,frame:n,frameSrc:s,payload:i}),n.dataset.turboReflexActive=!0,n.dataset.turboReflexElementId=e.id,e.tagName.toLowerCase()==="form")return A(e,i);t.preventDefault();let d=I(s);d.searchParams.set("turbo_reflex",JSON.stringify(i)),n.src=d.toString()}catch(i){console.error("TurboReflex encountered an unexpected error!",{element:e,frameId:r,frame:n,frameSrc:s,target:t.target},i),o.dispatch(o.error,e||document,{element:e,frameId:r,frame:n,frameSrc:s,error:i})}}p(C);u("change",["input","select","textarea"]);u("submit",["form"]);u("click",["*"]);var M={registerEvent:u,logRegisteredEvents:R,logLifecycleEventNames:o.logEventNames};export{M as default};
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/lifecycle_events.js", "../../javascript/frame_sources.js", "../../javascript/security.js", "../../javascript/elements.js", "../../javascript/event_registry.js", "../../javascript/turbo_reflex.js"],
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": "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",
6
- "names": ["events", "dispatch", "name", "target", "detail", "event", "logEventNames", "lifecycle_events_default", "frameSources", "event", "frame", "turboReflexActive", "turboReflexElementId", "element", "lifecycle_events_default", "Security", "security_default", "findClosestReflex", "element", "findClosestFrame", "findFrameId", "id", "frame", "lifecycle_events_default", "findFrame", "findFrameSrc", "frameSrc", "assignElementValueToPayload", "payload", "memo", "option", "buildAttributePayload", "attr", "registeredEvents", "eventListener", "registerEventListener", "fn", "registerEvent", "eventName", "tagNames", "isRegisteredEvent", "tagName", "logRegisteredEvents", "event", "frame", "turboReflexActive", "fetchOptions", "security_default", "buildURL", "urlString", "a", "invokeFormReflex", "form", "payload", "input", "invokeReflex", "element", "frameId", "frameSrc", "findClosestReflex", "isRegisteredEvent", "lifecycle_events_default", "findFrameId", "findFrame", "findFrameSrc", "buildAttributePayload", "frameURL", "error", "registerEventListener", "registerEvent", "turbo_reflex_default", "logRegisteredEvents"]
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 :perform_turbo_reflex, if: -> { turbo_reflex_requested? && turbo_reflex_valid? }
8
- after_action :append_turbo_reflex_turbo_streams, if: :turbo_reflex_performed?
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 turbo_reflex_meta_tag
15
- masked_token = turbo_reflex_message_verifier.generate(new_turbo_reflex_token)
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 turbo_reflex_params
21
- return ActionController::Parameters.new if params[:turbo_reflex].nil?
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
- return false unless client_turbo_reflex_token.present?
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
- !!@turbo_reflex_performed
25
+ turbo_reflex_runner.reflex_performed?
73
26
  end
74
27
 
75
28
  protected
76
29
 
77
- def perform_turbo_reflex
78
- @turbo_reflex_performed = true
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 append_turbo_reflex_content(content)
126
- sanitized_content = TurboReflex::Sanitizer.instance.sanitize(content).html_safe
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 }