turbo_boost-commands 0.0.10 → 0.0.12
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.
Potentially problematic release.
This version of turbo_boost-commands might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +167 -133
- data/app/assets/builds/@turbo-boost/commands.js +1 -5
- data/app/assets/builds/@turbo-boost/commands.js.map +4 -4
- data/app/assets/builds/@turbo-boost/commands.metafile.json +1 -0
- data/app/assets/images/hopsoft.webp +0 -0
- data/app/javascript/activity.js +4 -4
- data/app/javascript/confirmation.js +33 -0
- data/app/javascript/delegates.js +6 -8
- data/app/javascript/drivers/form.js +1 -1
- data/app/javascript/drivers/frame.js +1 -1
- data/app/javascript/drivers/index.js +4 -7
- data/app/javascript/drivers/method.js +1 -1
- data/app/javascript/drivers/window.js +15 -24
- data/app/javascript/elements.js +6 -8
- data/app/javascript/events.js +9 -7
- data/app/javascript/index.js +10 -9
- data/app/javascript/lifecycle.js +4 -12
- data/app/javascript/logger.js +4 -4
- data/app/javascript/meta.js +4 -4
- data/app/javascript/renderer.js +2 -2
- data/app/javascript/schema.js +2 -1
- data/app/javascript/state/index.js +5 -5
- data/app/javascript/state/observable.js +4 -5
- data/app/javascript/turbo.js +5 -18
- data/app/javascript/urls.js +1 -1
- data/app/javascript/uuids.js +2 -5
- data/lib/turbo_boost/commands/attribute_set.rb +1 -1
- data/lib/turbo_boost/commands/engine.rb +1 -1
- data/lib/turbo_boost/commands/runner.rb +27 -4
- data/lib/turbo_boost/commands/version.rb +1 -1
- metadata +8 -20
- data/app/javascript/drivers/turbo_form.js +0 -17
@@ -0,0 +1 @@
|
|
1
|
+
{"inputs":{"app/javascript/meta.js":{"bytes":337,"imports":[],"format":"esm"},"app/javascript/events.js":{"bytes":783,"imports":[{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/state/observable.js":{"bytes":920,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"},{"path":"app/javascript/events.js","kind":"import-statement","original":"../events"}],"format":"esm"},"app/javascript/state/index.js":{"bytes":1835,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"},{"path":"app/javascript/state/observable.js","kind":"import-statement","original":"./observable"},{"path":"app/javascript/events.js","kind":"import-statement","original":"../events"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/renderer.js":{"bytes":475,"imports":[],"format":"esm"},"app/javascript/activity.js":{"bytes":279,"imports":[],"format":"esm"},"app/javascript/lifecycle.js":{"bytes":610,"imports":[{"path":"app/javascript/activity.js","kind":"import-statement","original":"./activity"},{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"}],"format":"esm"},"app/javascript/turbo.js":{"bytes":1952,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"./meta"},{"path":"app/javascript/state/index.js","kind":"import-statement","original":"./state"},{"path":"app/javascript/renderer.js","kind":"import-statement","original":"./renderer"},{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"./lifecycle"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/schema.js":{"bytes":210,"imports":[{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/confirmation.js":{"bytes":947,"imports":[{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"},{"path":"app/javascript/schema.js","kind":"import-statement","original":"./schema"}],"format":"esm"},"app/javascript/delegates.js":{"bytes":858,"imports":[{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/elements.js":{"bytes":1451,"imports":[{"path":"app/javascript/schema.js","kind":"import-statement","original":"./schema"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"./lifecycle"}],"format":"esm"},"app/javascript/drivers/form.js":{"bytes":313,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"}],"format":"esm"},"app/javascript/urls.js":{"bytes":240,"imports":[],"format":"esm"},"app/javascript/drivers/frame.js":{"bytes":218,"imports":[{"path":"app/javascript/urls.js","kind":"import-statement","original":"../urls"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/drivers/method.js":{"bytes":265,"imports":[{"path":"app/javascript/urls.js","kind":"import-statement","original":"../urls"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/drivers/window.js":{"bytes":2074,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"},{"path":"app/javascript/state/index.js","kind":"import-statement","original":"../state"},{"path":"app/javascript/events.js","kind":"import-statement","original":"../events"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"../lifecycle"},{"path":"app/javascript/urls.js","kind":"import-statement","original":"../urls"},{"path":"app/javascript/renderer.js","kind":"import-statement","original":"../renderer"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/drivers/index.js":{"bytes":2044,"imports":[{"path":"app/javascript/elements.js","kind":"import-statement","original":"../elements"},{"path":"app/javascript/drivers/form.js","kind":"import-statement","original":"./form"},{"path":"app/javascript/drivers/frame.js","kind":"import-statement","original":"./frame"},{"path":"app/javascript/drivers/method.js","kind":"import-statement","original":"./method"},{"path":"app/javascript/drivers/window.js","kind":"import-statement","original":"./window"}],"format":"esm"},"app/javascript/logger.js":{"bytes":729,"imports":[{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"}],"format":"esm"},"app/javascript/uuids.js":{"bytes":202,"imports":[],"format":"esm"},"app/javascript/index.js":{"bytes":3478,"imports":[{"path":"app/javascript/turbo.js","kind":"import-statement","original":"./turbo"},{"path":"app/javascript/schema.js","kind":"import-statement","original":"./schema"},{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"},{"path":"app/javascript/activity.js","kind":"import-statement","original":"./activity"},{"path":"app/javascript/confirmation.js","kind":"import-statement","original":"./confirmation"},{"path":"app/javascript/delegates.js","kind":"import-statement","original":"./delegates"},{"path":"app/javascript/drivers/index.js","kind":"import-statement","original":"./drivers"},{"path":"app/javascript/meta.js","kind":"import-statement","original":"./meta"},{"path":"app/javascript/elements.js","kind":"import-statement","original":"./elements"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"./lifecycle"},{"path":"app/javascript/logger.js","kind":"import-statement","original":"./logger"},{"path":"app/javascript/state/index.js","kind":"import-statement","original":"./state"},{"path":"app/javascript/urls.js","kind":"import-statement","original":"./urls"},{"path":"app/javascript/uuids.js","kind":"import-statement","original":"./uuids"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"app/assets/builds/@turbo-boost/commands.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":37288},"app/assets/builds/@turbo-boost/commands.js":{"imports":[],"exports":["default"],"entryPoint":"app/javascript/index.js","inputs":{"app/javascript/meta.js":{"bytesInOutput":252},"app/javascript/events.js":{"bytesInOutput":500},"app/javascript/state/observable.js":{"bytesInOutput":405},"app/javascript/state/index.js":{"bytesInOutput":762},"app/javascript/renderer.js":{"bytesInOutput":263},"app/javascript/activity.js":{"bytesInOutput":173},"app/javascript/lifecycle.js":{"bytesInOutput":294},"app/javascript/turbo.js":{"bytesInOutput":1024},"app/javascript/schema.js":{"bytesInOutput":166},"app/javascript/confirmation.js":{"bytesInOutput":424},"app/javascript/delegates.js":{"bytesInOutput":436},"app/javascript/elements.js":{"bytesInOutput":782},"app/javascript/drivers/form.js":{"bytesInOutput":187},"app/javascript/urls.js":{"bytesInOutput":166},"app/javascript/drivers/frame.js":{"bytesInOutput":96},"app/javascript/drivers/method.js":{"bytesInOutput":130},"app/javascript/drivers/window.js":{"bytesInOutput":1329},"app/javascript/drivers/index.js":{"bytesInOutput":991},"app/javascript/logger.js":{"bytesInOutput":399},"app/javascript/uuids.js":{"bytesInOutput":154},"app/javascript/index.js":{"bytesInOutput":1656}},"bytes":11086}}}
|
Binary file
|
data/app/javascript/activity.js
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
const active = {}
|
2
2
|
|
3
|
-
function add
|
3
|
+
function add(payload) {
|
4
4
|
active[payload.id] = payload
|
5
5
|
}
|
6
6
|
|
7
|
-
function remove
|
7
|
+
function remove(id) {
|
8
8
|
delete active[id]
|
9
9
|
}
|
10
10
|
|
11
11
|
export default {
|
12
12
|
add,
|
13
13
|
remove,
|
14
|
-
get commands
|
14
|
+
get commands() {
|
15
15
|
return [...Object.values(active)]
|
16
16
|
},
|
17
|
-
get length
|
17
|
+
get length() {
|
18
18
|
return Object.keys(active).length
|
19
19
|
}
|
20
20
|
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { commandEvents } from './events'
|
2
|
+
import schema from './schema'
|
3
|
+
|
4
|
+
const confirmation = {
|
5
|
+
method: message => Promise.resolve(confirm(message))
|
6
|
+
}
|
7
|
+
|
8
|
+
const isTurboMethod = event => event.detail.driver === 'method'
|
9
|
+
|
10
|
+
const isTurboForm = event => {
|
11
|
+
if (event.detail.driver !== 'form') return false
|
12
|
+
|
13
|
+
const element = event.target
|
14
|
+
const frame = element.closest('turbo-frame')
|
15
|
+
const target = element.closest(`[${schema.frameAttribute}]`)
|
16
|
+
return !!(frame || target)
|
17
|
+
}
|
18
|
+
|
19
|
+
const shouldDelegate = event => isTurboMethod(event) || isTurboForm(event)
|
20
|
+
|
21
|
+
document.addEventListener(commandEvents.start, async event => {
|
22
|
+
const message = event.target.getAttribute(schema.confirmAttribute)
|
23
|
+
if (!message) return
|
24
|
+
|
25
|
+
event.detail.confirmation = true
|
26
|
+
|
27
|
+
if (shouldDelegate(event)) return // delegate confirmation handling to Turbo
|
28
|
+
|
29
|
+
const proceed = await confirmation.method(message)
|
30
|
+
if (!proceed) event.preventDefault()
|
31
|
+
})
|
32
|
+
|
33
|
+
export default confirmation
|
data/app/javascript/delegates.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
let events = []
|
2
2
|
let eventListener
|
3
3
|
|
4
|
-
function register
|
4
|
+
function register(eventName, selectors) {
|
5
5
|
const match = events.find(evt => evt.name === eventName)
|
6
6
|
if (match) events.splice(events.indexOf(match), 1)
|
7
7
|
events = [{ name: eventName, selectors }, ...events]
|
@@ -9,15 +9,13 @@ function register (eventName, selectors) {
|
|
9
9
|
return { ...events.find(evt => evt.name === eventName) }
|
10
10
|
}
|
11
11
|
|
12
|
-
function getRegisteredEventForElement
|
12
|
+
function getRegisteredEventForElement(element) {
|
13
13
|
return events.find(evt =>
|
14
|
-
evt.selectors.find(selector =>
|
15
|
-
Array.from(document.querySelectorAll(selector)).find(el => el === element)
|
16
|
-
)
|
14
|
+
evt.selectors.find(selector => Array.from(document.querySelectorAll(selector)).find(el => el === element))
|
17
15
|
)
|
18
16
|
}
|
19
17
|
|
20
|
-
function isRegisteredForElement
|
18
|
+
function isRegisteredForElement(eventName, element) {
|
21
19
|
const evt = getRegisteredEventForElement(element)
|
22
20
|
return evt && evt.name === eventName
|
23
21
|
}
|
@@ -25,10 +23,10 @@ function isRegisteredForElement (eventName, element) {
|
|
25
23
|
export default {
|
26
24
|
register,
|
27
25
|
isRegisteredForElement,
|
28
|
-
get events
|
26
|
+
get events() {
|
29
27
|
return [...events]
|
30
28
|
},
|
31
|
-
set handler
|
29
|
+
set handler(fn) {
|
32
30
|
eventListener = fn
|
33
31
|
}
|
34
32
|
}
|
@@ -4,14 +4,12 @@ import frameDriver from './frame'
|
|
4
4
|
import methodDriver from './method'
|
5
5
|
import windowDriver from './window'
|
6
6
|
|
7
|
-
function src
|
7
|
+
function src(element, frame) {
|
8
8
|
frame = frame || { dataset: {} }
|
9
|
-
return
|
10
|
-
element.href || frame.src || frame.dataset.turboBoostSrc || location.href
|
11
|
-
)
|
9
|
+
return element.href || frame.src || frame.dataset.turboBoostSrc || location.href
|
12
10
|
}
|
13
11
|
|
14
|
-
function find
|
12
|
+
function find(element) {
|
15
13
|
let frame = elements.findClosestFrameWithSource(element)
|
16
14
|
|
17
15
|
const { turboFrame, turboMethod } = element.dataset
|
@@ -50,8 +48,7 @@ function find (element) {
|
|
50
48
|
if ((!turboFrame || turboFrame === '_self') && frame)
|
51
49
|
return {
|
52
50
|
name: 'frame',
|
53
|
-
reason:
|
54
|
-
'element does NOT target a frame or targets _self and is contained by a frame',
|
51
|
+
reason: 'element does NOT target a frame or targets _self and is contained by a frame',
|
55
52
|
frame,
|
56
53
|
src: src(element, frame),
|
57
54
|
invokeCommand: frameDriver.invokeCommand
|
@@ -5,40 +5,37 @@ import lifecycle from '../lifecycle'
|
|
5
5
|
import urls from '../urls'
|
6
6
|
import renderer from '../renderer'
|
7
7
|
|
8
|
-
function aborted
|
8
|
+
function aborted(event) {
|
9
9
|
const xhr = event.target
|
10
10
|
dispatch(lifecycle.events.abort, document, {
|
11
11
|
detail: { ...event.detail, xhr }
|
12
12
|
})
|
13
13
|
}
|
14
14
|
|
15
|
-
function errored
|
15
|
+
function errored(event) {
|
16
16
|
const xhr = event.target
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
const append =
|
19
|
+
xhr.getResponseHeader('TurboBoost') === 'Append' ||
|
20
|
+
xhr.getResponseHeader('Content-Type').startsWith('text/vnd.turbo-boost.html')
|
21
|
+
append ? renderer.append(xhr.responseText) : renderer.replaceDocument(xhr.responseText)
|
21
22
|
|
22
23
|
const error = `Server returned a ${xhr.status} status code! TurboBoost Commands require 2XX-3XX status codes.`
|
23
24
|
|
24
|
-
dispatch(
|
25
|
-
lifecycle.events.clientError,
|
26
|
-
document,
|
27
|
-
{ detail: { ...event.detail, error, xhr } },
|
28
|
-
true
|
29
|
-
)
|
25
|
+
dispatch(lifecycle.events.clientError, document, { detail: { ...event.detail, error, xhr } }, true)
|
30
26
|
}
|
31
27
|
|
32
|
-
function loaded
|
28
|
+
function loaded(event) {
|
33
29
|
const xhr = event.target
|
34
30
|
if (xhr.status < 200 || xhr.status > 399) return errored(event)
|
35
31
|
const content = xhr.responseText
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
const append =
|
33
|
+
xhr.getResponseHeader('TurboBoost') === 'Append' ||
|
34
|
+
xhr.getResponseHeader('Content-Type').startsWith('text/vnd.turbo-boost.html')
|
35
|
+
append ? renderer.append(xhr.responseText) : renderer.replaceDocument(xhr.responseText)
|
39
36
|
}
|
40
37
|
|
41
|
-
function invokeCommand
|
38
|
+
function invokeCommand(payload) {
|
42
39
|
const src = payload.src
|
43
40
|
payload = { ...payload }
|
44
41
|
delete payload.src
|
@@ -46,16 +43,10 @@ function invokeCommand (payload) {
|
|
46
43
|
try {
|
47
44
|
const xhr = new XMLHttpRequest()
|
48
45
|
xhr.open('GET', urls.build(src, payload), true)
|
49
|
-
xhr.setRequestHeader(
|
50
|
-
'Accept',
|
51
|
-
'text/vnd.turbo-boost.html, text/html, application/xhtml+xml'
|
52
|
-
)
|
46
|
+
xhr.setRequestHeader('Accept', 'text/vnd.turbo-boost.html, text/html, application/xhtml+xml')
|
53
47
|
xhr.setRequestHeader('TurboBoost-Token', meta.token)
|
54
48
|
state.payloadChunks.forEach((chunk, i) =>
|
55
|
-
xhr.setRequestHeader(
|
56
|
-
`TurboBoost-State-${i.toString().padStart(4, '0')}`,
|
57
|
-
chunk
|
58
|
-
)
|
49
|
+
xhr.setRequestHeader(`TurboBoost-State-${i.toString().padStart(4, '0')}`, chunk)
|
59
50
|
)
|
60
51
|
|
61
52
|
xhr.addEventListener('abort', aborted)
|
data/app/javascript/elements.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
import schema from './schema'
|
2
2
|
import lifecycle from './lifecycle'
|
3
3
|
|
4
|
-
function findClosestCommand
|
4
|
+
function findClosestCommand(element) {
|
5
5
|
return element.closest(`[${schema.commandAttribute}]`)
|
6
6
|
}
|
7
7
|
|
8
|
-
function findClosestFrameWithSource
|
8
|
+
function findClosestFrameWithSource(element) {
|
9
9
|
return (
|
10
10
|
element.closest('turbo-frame[src]') ||
|
11
11
|
element.closest('turbo-frame[data-turbo-frame-src]') ||
|
@@ -13,12 +13,10 @@ function findClosestFrameWithSource (element) {
|
|
13
13
|
)
|
14
14
|
}
|
15
15
|
|
16
|
-
function assignElementValueToPayload
|
17
|
-
if (element.tagName.toLowerCase() !== 'select')
|
18
|
-
return (payload.value = element.value || null)
|
16
|
+
function assignElementValueToPayload(element, payload = {}) {
|
17
|
+
if (element.tagName.toLowerCase() !== 'select') return (payload.value = element.value || null)
|
19
18
|
|
20
|
-
if (!element.multiple)
|
21
|
-
return (payload.value = element.options[element.selectedIndex].value)
|
19
|
+
if (!element.multiple) return (payload.value = element.options[element.selectedIndex].value)
|
22
20
|
|
23
21
|
payload.values = Array.from(element.options).reduce((memo, option) => {
|
24
22
|
if (option.selected) memo.push(option.value)
|
@@ -26,7 +24,7 @@ function assignElementValueToPayload (element, payload = {}) {
|
|
26
24
|
}, [])
|
27
25
|
}
|
28
26
|
|
29
|
-
function buildAttributePayload
|
27
|
+
function buildAttributePayload(element) {
|
30
28
|
const payload = Array.from(element.attributes).reduce((memo, attr) => {
|
31
29
|
let value = attr.value
|
32
30
|
memo[attr.name] = value
|
data/app/javascript/events.js
CHANGED
@@ -14,11 +14,13 @@ export const stateEvents = {
|
|
14
14
|
|
15
15
|
export const allEvents = { ...commandEvents, ...stateEvents }
|
16
16
|
|
17
|
-
export function dispatch
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
export function dispatch(name, target, options = {}) {
|
18
|
+
return new Promise(resolve => {
|
19
|
+
options = options || {}
|
20
|
+
options.detail = options.detail || {}
|
21
|
+
target = target || document
|
22
|
+
const evt = new CustomEvent(name, { ...options, bubbles: true })
|
23
|
+
target.dispatchEvent(evt)
|
24
|
+
resolve(evt)
|
25
|
+
})
|
24
26
|
}
|
data/app/javascript/index.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
import '@turbo-boost/streams'
|
2
1
|
import './turbo'
|
3
2
|
import schema from './schema'
|
4
3
|
import { dispatch, commandEvents, stateEvents } from './events'
|
5
4
|
import activity from './activity'
|
5
|
+
import confirmation from './confirmation'
|
6
6
|
import delegates from './delegates'
|
7
7
|
import drivers from './drivers'
|
8
8
|
import meta from './meta'
|
@@ -13,17 +13,17 @@ import state from './state'
|
|
13
13
|
import urls from './urls'
|
14
14
|
import uuids from './uuids'
|
15
15
|
|
16
|
-
function buildCommandPayload
|
16
|
+
function buildCommandPayload(id, element) {
|
17
17
|
return {
|
18
18
|
id, // uniquely identifies the command
|
19
19
|
name: element.getAttribute(schema.commandAttribute),
|
20
20
|
elementId: element.id.length > 0 ? element.id : null,
|
21
21
|
elementAttributes: elements.buildAttributePayload(element),
|
22
|
-
startedAt:
|
22
|
+
startedAt: Date.now()
|
23
23
|
}
|
24
24
|
}
|
25
25
|
|
26
|
-
function invokeCommand
|
26
|
+
async function invokeCommand(event) {
|
27
27
|
let element
|
28
28
|
let payload = {}
|
29
29
|
|
@@ -41,12 +41,12 @@ function invokeCommand (event) {
|
|
41
41
|
src: driver.src
|
42
42
|
}
|
43
43
|
|
44
|
-
const startEvent = dispatch(commandEvents.start, element, {
|
44
|
+
const startEvent = await dispatch(commandEvents.start, element, {
|
45
45
|
cancelable: true,
|
46
46
|
detail: payload
|
47
47
|
})
|
48
48
|
|
49
|
-
if (startEvent.defaultPrevented)
|
49
|
+
if (startEvent.defaultPrevented || (startEvent.detail.confirmation && event.defaultPrevented))
|
50
50
|
return dispatch(commandEvents.abort, element, {
|
51
51
|
detail: {
|
52
52
|
message: `An event handler for '${commandEvents.start}' prevented default behavior and blocked command invocation!`,
|
@@ -104,21 +104,22 @@ self.TurboBoost = {
|
|
104
104
|
|
105
105
|
stateEvents,
|
106
106
|
|
107
|
-
get state
|
107
|
+
get state() {
|
108
108
|
return state.current
|
109
109
|
},
|
110
110
|
|
111
|
-
get stateDelta
|
111
|
+
get stateDelta() {
|
112
112
|
return state.delta
|
113
113
|
}
|
114
114
|
}
|
115
115
|
|
116
116
|
self.TurboBoost.Commands = {
|
117
|
+
confirmation,
|
117
118
|
logger,
|
118
119
|
schema,
|
119
120
|
events: commandEvents,
|
120
121
|
registerEventDelegate: delegates.register,
|
121
|
-
get eventDelegates
|
122
|
+
get eventDelegates() {
|
122
123
|
return delegates.events
|
123
124
|
}
|
124
125
|
}
|
data/app/javascript/lifecycle.js
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
import activity from './activity'
|
2
2
|
import { dispatch, commandEvents } from './events'
|
3
3
|
|
4
|
-
function finish
|
5
|
-
event.detail.endedAt =
|
4
|
+
function finish(event) {
|
5
|
+
event.detail.endedAt = Date.now()
|
6
6
|
event.detail.milliseconds = event.detail.endedAt - event.detail.startedAt
|
7
|
-
setTimeout(
|
8
|
-
() =>
|
9
|
-
dispatch(commandEvents.finish, event.target, { detail: event.detail }),
|
10
|
-
25
|
11
|
-
)
|
7
|
+
setTimeout(() => dispatch(commandEvents.finish, event.target, { detail: event.detail }), 25)
|
12
8
|
}
|
13
9
|
|
14
10
|
// TODO: forward source event to finish (error or success)
|
15
11
|
addEventListener(commandEvents.serverError, finish)
|
16
12
|
addEventListener(commandEvents.success, finish)
|
17
|
-
addEventListener(
|
18
|
-
commandEvents.finish,
|
19
|
-
event => activity.remove(event.detail.id),
|
20
|
-
true
|
21
|
-
)
|
13
|
+
addEventListener(commandEvents.finish, event => activity.remove(event.detail.id), true)
|
22
14
|
|
23
15
|
export default { events: commandEvents }
|
data/app/javascript/logger.js
CHANGED
@@ -13,17 +13,17 @@ const logLevels = {
|
|
13
13
|
Object.values(events).forEach(name => {
|
14
14
|
addEventListener(name, event => {
|
15
15
|
if (logLevels[currentLevel].includes(event.type)) {
|
16
|
-
const
|
17
|
-
console[
|
16
|
+
const { target, detail } = event
|
17
|
+
console[currentLevel](event.type, { target, detail })
|
18
18
|
}
|
19
19
|
})
|
20
20
|
})
|
21
21
|
|
22
22
|
export default {
|
23
|
-
get level
|
23
|
+
get level() {
|
24
24
|
return currentLevel
|
25
25
|
},
|
26
|
-
set level
|
26
|
+
set level(value) {
|
27
27
|
if (!Object.keys(logLevels).includes(value)) value = 'unknown'
|
28
28
|
return (currentLevel = value)
|
29
29
|
}
|
data/app/javascript/meta.js
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
class Meta {
|
2
|
-
get element
|
2
|
+
get element() {
|
3
3
|
return document.querySelector('meta[name="turbo-boost"]')
|
4
4
|
}
|
5
5
|
|
6
|
-
get token
|
6
|
+
get token() {
|
7
7
|
return this.element.getAttribute('content')
|
8
8
|
}
|
9
9
|
|
10
|
-
get busy
|
10
|
+
get busy() {
|
11
11
|
return this.element.dataset.busy === 'true'
|
12
12
|
}
|
13
13
|
|
14
|
-
set busy
|
14
|
+
set busy(value) {
|
15
15
|
return (this.element.dataset.busy = !!value)
|
16
16
|
}
|
17
17
|
}
|
data/app/javascript/renderer.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
function replaceDocument
|
1
|
+
function replaceDocument(content) {
|
2
2
|
const head = '<html'
|
3
3
|
const tail = '</html'
|
4
4
|
const headIndex = content.indexOf(head)
|
@@ -9,7 +9,7 @@ function replaceDocument (content) {
|
|
9
9
|
}
|
10
10
|
}
|
11
11
|
|
12
|
-
function append
|
12
|
+
function append(content) {
|
13
13
|
document.body.insertAdjacentHTML('beforeend', content)
|
14
14
|
}
|
15
15
|
|
data/app/javascript/schema.js
CHANGED
@@ -5,7 +5,7 @@ import { dispatch, commandEvents, stateEvents } from '../events'
|
|
5
5
|
let loadedState, currentState, changedState
|
6
6
|
let loadStateTimeout
|
7
7
|
|
8
|
-
function loadState
|
8
|
+
function loadState() {
|
9
9
|
if (!meta.element) return loadStateLater()
|
10
10
|
const json = atob(meta.element.dataset.state)
|
11
11
|
changedState = {}
|
@@ -19,7 +19,7 @@ function loadState () {
|
|
19
19
|
)
|
20
20
|
}
|
21
21
|
|
22
|
-
function loadStateLater
|
22
|
+
function loadStateLater() {
|
23
23
|
clearTimeout(loadStateTimeout)
|
24
24
|
loadStateTimeout = setTimeout(loadState, 10)
|
25
25
|
}
|
@@ -43,11 +43,11 @@ addEventListener(stateEvents.stateChange, event => {
|
|
43
43
|
export default {
|
44
44
|
events: stateEvents,
|
45
45
|
|
46
|
-
get current
|
46
|
+
get current() {
|
47
47
|
return currentState
|
48
48
|
},
|
49
49
|
|
50
|
-
get delta
|
50
|
+
get delta() {
|
51
51
|
return changedState
|
52
52
|
},
|
53
53
|
|
@@ -56,7 +56,7 @@ export default {
|
|
56
56
|
// A Base64 character is an 8-bit-padded ASCII character... or 1 byte
|
57
57
|
//
|
58
58
|
// SEE: lib/state.rb - for info on how `state` is serialized/deserialized
|
59
|
-
get payloadChunks
|
59
|
+
get payloadChunks() {
|
60
60
|
return btoa(JSON.stringify(changedState)).match(/.{1,2000}/g)
|
61
61
|
}
|
62
62
|
}
|
@@ -3,17 +3,17 @@ import { dispatch, stateEvents as events } from '../events'
|
|
3
3
|
|
4
4
|
let head
|
5
5
|
|
6
|
-
function observable
|
6
|
+
function observable(object, parent = null) {
|
7
7
|
if (!object || typeof object !== 'object') return object
|
8
8
|
|
9
9
|
const proxy = new Proxy(object, {
|
10
|
-
deleteProperty
|
10
|
+
deleteProperty(target, key) {
|
11
11
|
delete target[key]
|
12
12
|
dispatch(events.stateChange, meta.element, { detail: { state: head } })
|
13
13
|
return true
|
14
14
|
},
|
15
15
|
|
16
|
-
set
|
16
|
+
set(target, key, value, receiver) {
|
17
17
|
target[key] = observable(value, this)
|
18
18
|
dispatch(events.stateChange, meta.element, { detail: { state: head } })
|
19
19
|
return true
|
@@ -23,8 +23,7 @@ function observable (object, parent = null) {
|
|
23
23
|
if (Array.isArray(object)) {
|
24
24
|
object.forEach((value, index) => (object[index] = observable(value, proxy)))
|
25
25
|
} else if (typeof object === 'object') {
|
26
|
-
for (const [key, value] of Object.entries(object))
|
27
|
-
object[key] = observable(value, proxy)
|
26
|
+
for (const [key, value] of Object.entries(object)) object[key] = observable(value, proxy)
|
28
27
|
}
|
29
28
|
|
30
29
|
if (!parent) head = proxy
|
data/app/javascript/turbo.js
CHANGED
@@ -13,22 +13,15 @@ addEventListener('turbo:before-fetch-request', event => {
|
|
13
13
|
|
14
14
|
// command invoked and busy
|
15
15
|
if (meta.busy) {
|
16
|
-
let acceptHeaders = [
|
17
|
-
|
18
|
-
fetchOptions.headers['Accept']
|
19
|
-
]
|
20
|
-
acceptHeaders = acceptHeaders
|
21
|
-
.filter(entry => entry && entry.trim().length > 0)
|
22
|
-
.join(', ')
|
16
|
+
let acceptHeaders = ['text/vnd.turbo-boost.html', fetchOptions.headers['Accept']]
|
17
|
+
acceptHeaders = acceptHeaders.filter(entry => entry && entry.trim().length > 0).join(', ')
|
23
18
|
fetchOptions.headers['Accept'] = acceptHeaders
|
24
19
|
fetchOptions.headers['TurboBoost-Token'] = meta.token
|
25
20
|
}
|
26
21
|
|
27
22
|
// always send state
|
28
23
|
state.payloadChunks.forEach((chunk, i) => {
|
29
|
-
fetchOptions.headers[
|
30
|
-
`TurboBoost-State-${i.toString().padStart(4, '0')}`
|
31
|
-
] = chunk
|
24
|
+
fetchOptions.headers[`TurboBoost-State-${i.toString().padStart(4, '0')}`] = chunk
|
32
25
|
})
|
33
26
|
})
|
34
27
|
|
@@ -42,12 +35,7 @@ addEventListener('turbo:before-fetch-response', event => {
|
|
42
35
|
if (response.header('TurboBoost')) {
|
43
36
|
if (response.statusCode < 200 || response.statusCode > 399) {
|
44
37
|
const error = `Server returned a ${response.statusCode} status code! TurboBoost Commands require 2XX-3XX status codes.`
|
45
|
-
dispatch(
|
46
|
-
lifecycle.events.clientError,
|
47
|
-
document,
|
48
|
-
{ detail: { ...event.detail, error } },
|
49
|
-
true
|
50
|
-
)
|
38
|
+
dispatch(lifecycle.events.clientError, document, { detail: { ...event.detail, error } }, true)
|
51
39
|
}
|
52
40
|
|
53
41
|
if (response.header('TurboBoost') === 'Append') {
|
@@ -60,7 +48,6 @@ addEventListener('turbo:before-fetch-response', event => {
|
|
60
48
|
// fires when a frame element is navigated and finishes loading
|
61
49
|
addEventListener('turbo:frame-load', event => {
|
62
50
|
const frame = event.target.closest('turbo-frame')
|
63
|
-
frame.dataset.turboBoostSrc =
|
64
|
-
frameSources[frame.id] || frame.src || frame.dataset.turboBoostSrc
|
51
|
+
frame.dataset.turboBoostSrc = frameSources[frame.id] || frame.src || frame.dataset.turboBoostSrc
|
65
52
|
delete frameSources[frame.id]
|
66
53
|
})
|
data/app/javascript/urls.js
CHANGED
data/app/javascript/uuids.js
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
function v4
|
1
|
+
function v4() {
|
2
2
|
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
|
3
|
-
(
|
4
|
-
c ^
|
5
|
-
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
|
6
|
-
).toString(16)
|
3
|
+
(c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
|
7
4
|
)
|
8
5
|
}
|
9
6
|
|
@@ -21,7 +21,7 @@ class TurboBoost::Commands::AttributeSet
|
|
21
21
|
name.delete_prefix!("#{prefix}_") unless prefix.blank?
|
22
22
|
|
23
23
|
# type casting
|
24
|
-
value = value.to_i if value.is_a?(String) && value.match?(/\A
|
24
|
+
value = value.to_i if value.is_a?(String) && value.match?(/\A-?\d+\z/)
|
25
25
|
value = value == "true" if value.is_a?(String) && value.match?(/\A(true|false)\z/i)
|
26
26
|
|
27
27
|
instance_variable_set "@#{name}", value
|
@@ -19,7 +19,7 @@ module TurboBoost::Commands
|
|
19
19
|
class Engine < ::Rails::Engine
|
20
20
|
config.turbo_boost_commands = ActiveSupport::OrderedOptions.new
|
21
21
|
config.turbo_boost_commands[:max_cookie_size] = ActionDispatch::Cookies::MAX_COOKIE_SIZE / 3
|
22
|
-
config.turbo_boost_commands[:validate_client_token] =
|
22
|
+
config.turbo_boost_commands[:validate_client_token] = false
|
23
23
|
|
24
24
|
# must opt-in to state overrides
|
25
25
|
config.turbo_boost_commands[:apply_client_state_overrides] = false
|