-
-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathbridge.js
96 lines (84 loc) · 4.11 KB
/
bridge.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { isValidVersion } from './utils'
export default class Bridge {
init () {
// Tag all cloaked elements on first page load.
document.body.querySelectorAll('[x-cloak]').forEach((node) => {
node.setAttribute('data-alpine-was-cloaked', '')
})
this.configureEventHandlers()
}
setMutationObserverState (state) {
if (!window.Alpine.version || !isValidVersion('2.4.0', window.Alpine.version)) {
throw new Error('Invalid Alpine version. Please use Alpine 2.4.0 or above')
}
window.Alpine.pauseMutationObserver = state
}
configureEventHandlers () {
// Once Turbolinks finished is magic, we initialise Alpine on the new page
// and resume the observer
const initCallback = () => {
window.Alpine.discoverUninitializedComponents((el) => {
window.Alpine.initializeComponent(el)
})
requestAnimationFrame(() => { this.setMutationObserverState(false) })
}
// Before swapping the body, clean up any element with x-turbolinks-cached
// which do not have any Alpine properties.
// Note, at this point all html fragments marked as data-turbolinks-permanent
// are already copied over from the previous page so they retain their listener
// and custom properties and we don't want to reset them.
const beforeRenderCallback = (event) => {
const newBody = event.data ? event.data.newBody : event.detail.newBody
newBody.querySelectorAll('[data-alpine-generated-me],[x-cloak]').forEach((el) => {
if (el.hasAttribute('x-cloak')) {
// When we get a new document body tag any cloaked elements so we can cloak
// them again before caching.
el.setAttribute('data-alpine-was-cloaked', '')
}
if (el.hasAttribute('data-alpine-generated-me')) {
el.removeAttribute('data-alpine-generated-me')
if (typeof el.__x_for_key === 'undefined' && typeof el.__x_inserted_me === 'undefined') {
el.remove()
}
}
})
}
// Pause the the mutation observer to avoid data races, it will be resumed by the turbolinks:load event.
// We mark as `data-alpine-generated-m` all elements that are crated by an Alpine templating directives.
// The reason is that turbolinks caches pages using cloneNode which removes listeners and custom properties
// So we need to propagate this infomation using a HTML attribute. I know, not ideal but I could not think
// of a better option.
// Note, we can't remove any Alpine generated element as yet because if they live inside an element
// marked as data-turbolinks-permanent they need to be copied into the next page.
// The coping process happens somewhere between before-cache and before-render.
const beforeCacheCallback = () => {
this.setMutationObserverState(true)
document.body.querySelectorAll('[x-for],[x-if],[data-alpine-was-cloaked]').forEach((el) => {
// Cloak any elements again that were tagged when the page was loaded
if (el.hasAttribute('data-alpine-was-cloaked')) {
el.setAttribute('x-cloak', '')
el.removeAttribute('data-alpine-was-cloaked')
}
if (el.hasAttribute('x-for')) {
let nextEl = el.nextElementSibling
while (nextEl && typeof nextEl.__x_for_key !== 'undefined') {
const currEl = nextEl
nextEl = nextEl.nextElementSibling
currEl.setAttribute('data-alpine-generated-me', true)
}
} else if (el.hasAttribute('x-if')) {
const ifEl = el.nextElementSibling
if (ifEl && typeof ifEl.__x_inserted_me !== 'undefined') {
ifEl.setAttribute('data-alpine-generated-me', true)
}
}
})
}
document.addEventListener('turbo:load', initCallback)
document.addEventListener('turbolinks:load', initCallback)
document.addEventListener('turbo:before-render', beforeRenderCallback)
document.addEventListener('turbolinks:before-render', beforeRenderCallback)
document.addEventListener('turbo:before-cache', beforeCacheCallback)
document.addEventListener('turbolinks:before-cache', beforeCacheCallback)
}
}