turbo_reflex 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 120854ae5417fe826b11a24e87acd266f390444c7d334861e315bf3a955406e3
4
- data.tar.gz: de07d29af7d13096cca47abf76de7604eb758a3246ddd35951db6664b5d17005
3
+ metadata.gz: 6d777dc777c4797542c2a285da1ba2ee9ff75e3a329b90f148abee3691da210f
4
+ data.tar.gz: cda1b302d539fe5f3081ce6722336eb663d2fa2c2881cc5b48ae65fe24e400b7
5
5
  SHA512:
6
- metadata.gz: 9323c7d812807dfcb3a881f26a2ba7e17cd9425440ab12ec9a5e0af335384d6decb4259208c2c5bfcd272182b39ef3478e9088d0d0872e9d4637d86cbd1d53cd
7
- data.tar.gz: c4878c6927fa6c1b5608e91b58f828bd49c5fe1078cd989455d477f232bfb8199df61acc84e2fa0869496f54b804573cfa6667ecdf129ae7064c76a2d3c41c79
6
+ metadata.gz: 584be627cda098a3aca5471e841c3c3da3b0b4eb51891e69570b6ab330b82f80977b604f288f4955895a6dbd21ae2c4661c814982e8f4a05168bbc9a7220625f
7
+ data.tar.gz: 42820cbb510fb61ef22c07a550831d69cae13f08867d15c626ed0f6dce045440d3ca10e0668d5e75671e8d5db04a58d75cecf091e9d11370d219f89d10b39fed
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.7)
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/
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-672-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"},u={...S};var v={};function D(e){v[e.id]=e}function H(e){delete v[e]}var b={add:D,remove:H,get reflexes(){return[...Object.values(v)]},get length(){return Object.keys(v).length}};var f={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(f.clientError,t,{error:s,...r},!0)}}function w(e){e.detail.endedAt=new Date().getTime(),e.detail.milliseconds=e.detail.endedAt-e.detail.startedAt,setTimeout(()=>p(f.finish,e.target,e.detail),10)}addEventListener(f.serverError,w);addEventListener(f.success,w);addEventListener(f.finish,e=>b.remove(e.detail.id),!0);var o={dispatch:p,events:f};function _(e){return e.closest(`[${u.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,c)=>{let i=c.value;return typeof i=="string"&&i.length>100&&(i=i.slice(0,100)+"..."),s[c.name]=i,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[u.reflexAttribute],delete n[u.frameAttribute],n}var a={buildAttributePayload:V,findClosestReflex:_,findClosestFrame:q,get metaElement(){return document.getElementById("turbo-reflex")},get metaElementToken(){return document.getElementById("turbo-reflex").getAttribute("content")}};var E={};addEventListener("turbo:before-fetch-request",e=>{let t=e.target.closest("turbo-frame"),{fetchOptions:r}=e.detail;r.headers["TurboReflex-Token"]=a.metaElementToken});addEventListener("turbo:before-fetch-response",e=>{let t=e.target.closest("turbo-frame");t&&(E[t.id]=t.src)});addEventListener("turbo:frame-load",e=>{let t=e.target.closest("turbo-frame");t.dataset.turboReflexSrc=E[t.id]||t.src||t.dataset.turboReflexSrc,delete E[t.id]});var m={},T;function $(e,t){m[e]=t,document.addEventListener(e,T,!0)}function N(e,t){return t=t.toLowerCase(),m[e].includes(t)||!Object.values(m).flat().includes(t)&&m[e].includes("*")}var l={events:m,register:$,isRegistered:N,set handler(e){T=e}};function B(e,t={}){t.token=a.metaElementToken;let r=document.createElement("input");r.type="hidden",r.name="turbo_reflex",r.value=JSON.stringify(t),e.appendChild(r)}var y={invokeReflex:B};function F(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:F};function M(e,t){let r=t.src;t={...t},delete t.src,e.src=g.build(r,t)}var k={invokeReflex:M};function U(e){let t=e.target;o.dispatch(o.events.abort,window,{xhr:t,...e.detail})}function R(e){let t=e.target;o.dispatch(o.events.clientError,window,{xhr:t,...e.detail,error:`Server returned a ${t.status} status code! TurboReflex requires 2XX status codes. Server message: ${t.statusText}`},!0)}function X(e){let t=e.target,r=t.responseText,n=t.getResponseHeader("TurboReflex-Hijacked")==="true";if((t.status<200||t.status>299)&&R(e),n){let s="<turbo-stream",c="</turbo-stream>",i=r.indexOf(s),d=r.lastIndexOf(c);if(i>=0&&d>=0){let h=r.slice(i,d+c.length);document.body.insertAdjacentHTML("beforeend",h)}}else{let s="<html",c="</html",i=r.indexOf(s),d=r.lastIndexOf(c);if(i>=0&&d>=0){let h=r.slice(r.indexOf(">",i)+1,d);document.documentElement.innerHTML=h}}}function J(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",a.metaElementToken),r.addEventListener("abort",U),r.addEventListener("error",R),r.addEventListener("load",X),r.send()}catch(r){let n=`Unexpected error sending HTTP request! ${r.message}`;R(r,{detail:{message:n}})}}var A={invokeReflex:J};function L(e,t){return t=t||{dataset:{}},e.href||t.src||t.dataset.turboReflexSrc||location.href}function G(e){let t=a.findClosestFrame(e),r=e.dataset.turboFrame;return e.tagName.toLowerCase()==="form"?{name:"form",reason:"Element is a form.",frame:t,src:e.action,invokeReflex:y.invokeReflex}:r&&r!=="_self"?(t=document.getElementById(r),{name:"frame",reason:"element targets a frame that is not _self",frame:t,src:L(e,t),invokeReflex:k.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:L(e,t),invokeReflex:k.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:L(e),invokeReflex:A.invokeReflex}}var O={find:G};var x="unknown",I={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=>{I[x].includes(t.type)&&console[x==="debug"?"log":x](t.type,t.detail)})});var j={get level(){return x},set level(e){return Object.keys(I).includes(e)||(e="unknown"),x=e}};function z(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,e=>(e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16))}var C={v4:z};function K(e){let t,r={};try{if(t=a.findClosestReflex(e.target),!t||!l.isRegistered(e.type,t.tagName))return;let n=O.find(t);switch(r={id:`reflex-${C.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:a.buildAttributePayload(t),startedAt:new Date().getTime()},b.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})}}l.handler=K;l.register("change",["input","select","textarea"]);l.register("submit",["form"]);l.register("click",["*"]);var qe={schema:u,logger:j,registerEventDelegate:l.register,get eventDelegates(){return{...l.events}},get lifecycleEvents(){return[...Object.values(o.events)]}};export{qe 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/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 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 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", "import elements from './elements'\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 if (frame) frameSources[frame.id] = frame.src\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'\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 lifecycle.dispatch(\n lifecycle.events.clientError,\n window,\n {\n xhr,\n ...event.detail,\n error: `Server returned a ${xhr.status} status code! TurboReflex requires 2XX status codes. Server message: ${xhr.statusText}`\n },\n true\n )\n}\n\nfunction loaded (event) {\n const xhr = event.target\n const content = xhr.responseText\n const hijacked = xhr.getResponseHeader('TurboReflex-Hijacked') === 'true'\n if (xhr.status < 200 || xhr.status > 299) errored(event)\n\n if (hijacked) {\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 } else {\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}\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", "finish", "activity_default", "lifecycle_default", "findClosestReflex", "element", "schema_default", "findClosestFrame", "assignElementValueToPayload", "payload", "memo", "option", "buildAttributePayload", "attr", "value", "elements_default", "frameSources", "event", "frame", "fetchOptions", "elements_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", "loaded", "content", "hijacked", "head", "tail", "headIndex", "tailIndex", "streams", "html", "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,70 @@
1
+ import elements from '../elements'
2
+ import lifecycle from '../lifecycle'
3
+ import urls from '../urls'
4
+
5
+ function aborted (event) {
6
+ const xhr = event.target
7
+ lifecycle.dispatch(lifecycle.events.abort, window, { xhr, ...event.detail })
8
+ }
9
+
10
+ function errored (event) {
11
+ const xhr = event.target
12
+ lifecycle.dispatch(
13
+ lifecycle.events.clientError,
14
+ window,
15
+ {
16
+ xhr,
17
+ ...event.detail,
18
+ error: `Server returned a ${xhr.status} status code! TurboReflex requires 2XX status codes. Server message: ${xhr.statusText}`
19
+ },
20
+ true
21
+ )
22
+ }
23
+
24
+ function loaded (event) {
25
+ const xhr = event.target
26
+ const content = xhr.responseText
27
+ const hijacked = xhr.getResponseHeader('TurboReflex-Hijacked') === 'true'
28
+ if (xhr.status < 200 || xhr.status > 299) errored(event)
29
+
30
+ if (hijacked) {
31
+ const head = '<turbo-stream'
32
+ const tail = '</turbo-stream>'
33
+ const headIndex = content.indexOf(head)
34
+ const tailIndex = content.lastIndexOf(tail)
35
+ if (headIndex >= 0 && tailIndex >= 0) {
36
+ const streams = content.slice(headIndex, tailIndex + tail.length)
37
+ document.body.insertAdjacentHTML('beforeend', streams)
38
+ }
39
+ } else {
40
+ const head = '<html'
41
+ const tail = '</html'
42
+ const headIndex = content.indexOf(head)
43
+ const tailIndex = content.lastIndexOf(tail)
44
+ if (headIndex >= 0 && tailIndex >= 0) {
45
+ const html = content.slice(content.indexOf('>', headIndex) + 1, tailIndex)
46
+ document.documentElement.innerHTML = html
47
+ }
48
+ }
49
+ }
50
+
51
+ function invokeReflex (payload) {
52
+ const src = payload.src
53
+ payload = { ...payload }
54
+ delete payload.src
55
+
56
+ try {
57
+ const xhr = new XMLHttpRequest()
58
+ xhr.open('GET', urls.build(src, payload), true)
59
+ xhr.setRequestHeader('TurboReflex-Token', elements.metaElementToken)
60
+ xhr.addEventListener('abort', aborted)
61
+ xhr.addEventListener('error', errored)
62
+ xhr.addEventListener('load', loaded)
63
+ xhr.send()
64
+ } catch (ex) {
65
+ const message = `Unexpected error sending HTTP request! ${ex.message}`
66
+ errored(ex, { detail: { message } })
67
+ }
68
+ }
69
+
70
+ export default { invokeReflex }