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.

@@ -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
@@ -1,20 +1,20 @@
1
1
  const active = {}
2
2
 
3
- function add (payload) {
3
+ function add(payload) {
4
4
  active[payload.id] = payload
5
5
  }
6
6
 
7
- function remove (id) {
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
@@ -1,7 +1,7 @@
1
1
  let events = []
2
2
  let eventListener
3
3
 
4
- function register (eventName, selectors) {
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 (element) {
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 (eventName, element) {
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 (fn) {
29
+ set handler(fn) {
32
30
  eventListener = fn
33
31
  }
34
32
  }
@@ -1,6 +1,6 @@
1
1
  import meta from '../meta'
2
2
 
3
- function invokeCommand (form, payload = {}) {
3
+ function invokeCommand(form, payload = {}) {
4
4
  payload.token = meta.token
5
5
  const input = document.createElement('input')
6
6
  input.type = 'hidden'
@@ -1,6 +1,6 @@
1
1
  import urls from '../urls'
2
2
 
3
- function invokeCommand (frame, payload) {
3
+ function invokeCommand(frame, payload) {
4
4
  const src = payload.src
5
5
  payload = { ...payload }
6
6
  delete payload.src
@@ -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 (element, frame) {
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 (element) {
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
@@ -1,6 +1,6 @@
1
1
  import urls from '../urls'
2
2
 
3
- function invokeCommand (element, payload = {}) {
3
+ function invokeCommand(element, payload = {}) {
4
4
  const src = payload.src
5
5
  payload = { ...payload }
6
6
  delete payload.src
@@ -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 (event) {
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 (event) {
15
+ function errored(event) {
16
16
  const xhr = event.target
17
17
 
18
- xhr.getResponseHeader('TurboBoost') === 'Append'
19
- ? renderer.append(xhr.responseText)
20
- : renderer.replaceDocument(xhr.responseText)
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 (event) {
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
- xhr.getResponseHeader('TurboBoost') === 'Append'
37
- ? renderer.append(xhr.responseText)
38
- : renderer.replaceDocument(xhr.responseText)
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 (payload) {
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)
@@ -1,11 +1,11 @@
1
1
  import schema from './schema'
2
2
  import lifecycle from './lifecycle'
3
3
 
4
- function findClosestCommand (element) {
4
+ function findClosestCommand(element) {
5
5
  return element.closest(`[${schema.commandAttribute}]`)
6
6
  }
7
7
 
8
- function findClosestFrameWithSource (element) {
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 (element, payload = {}) {
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 (element) {
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
@@ -14,11 +14,13 @@ export const stateEvents = {
14
14
 
15
15
  export const allEvents = { ...commandEvents, ...stateEvents }
16
16
 
17
- export function dispatch (name, target, options = {}) {
18
- options = options || {}
19
- options.detail = options.detail || {}
20
- target = target || document
21
- const evt = new CustomEvent(name, { ...options, bubbles: true })
22
- target.dispatchEvent(evt)
23
- return evt
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
  }
@@ -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 (id, element) {
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: new Date().getTime()
22
+ startedAt: Date.now()
23
23
  }
24
24
  }
25
25
 
26
- function invokeCommand (event) {
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
  }
@@ -1,23 +1,15 @@
1
1
  import activity from './activity'
2
2
  import { dispatch, commandEvents } from './events'
3
3
 
4
- function finish (event) {
5
- event.detail.endedAt = new Date().getTime()
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 }
@@ -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 level = currentLevel === 'debug' ? 'log' : currentLevel
17
- console[level](event.type, { target: event.target, detail: event.detail })
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 (value) {
26
+ set level(value) {
27
27
  if (!Object.keys(logLevels).includes(value)) value = 'unknown'
28
28
  return (currentLevel = value)
29
29
  }
@@ -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 (value) {
14
+ set busy(value) {
15
15
  return (this.element.dataset.busy = !!value)
16
16
  }
17
17
  }
@@ -1,4 +1,4 @@
1
- function replaceDocument (content) {
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 (content) {
12
+ function append(content) {
13
13
  document.body.insertAdjacentHTML('beforeend', content)
14
14
  }
15
15
 
@@ -1,7 +1,8 @@
1
1
  const schema = {
2
2
  frameAttribute: 'data-turbo-frame',
3
3
  methodAttribute: 'data-turbo-method',
4
- commandAttribute: 'data-turbo-command'
4
+ commandAttribute: 'data-turbo-command',
5
+ confirmAttribute: 'data-turbo-confirm'
5
6
  }
6
7
 
7
8
  export default { ...schema }
@@ -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 (object, parent = null) {
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 (target, key) {
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 (target, key, value, receiver) {
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
@@ -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
- 'text/vnd.turbo-boost.html',
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
  })
@@ -1,4 +1,4 @@
1
- function build (urlString, payload = {}) {
1
+ function build(urlString, payload = {}) {
2
2
  const a = document.createElement('a')
3
3
  a.href = urlString
4
4
  const url = new URL(a)
@@ -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\d+\z/)
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] = true
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