saloe 0.0.88 → 0.0.90
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.
- package/dist/listener.cjs.js +3 -1
- package/dist/listener.es.js +3 -1
- package/package.json +1 -1
- package/src/constants.js +293 -0
- package/src/listener.js +2 -285
package/dist/listener.cjs.js
CHANGED
|
@@ -78,6 +78,7 @@ const listener = ({
|
|
|
78
78
|
const script = document == null ? void 0 : document.createElement("script");
|
|
79
79
|
(_a = Object.keys(attrs)) == null ? void 0 : _a.forEach((attrKey) => script == null ? void 0 : script.setAttribute(attrKey, attrs[attrKey]));
|
|
80
80
|
script.id = id;
|
|
81
|
+
script.type = "module";
|
|
81
82
|
if (content) script == null ? void 0 : script.insertAdjacentHTML("beforeend", content);
|
|
82
83
|
return new Promise((resolve, reject) => {
|
|
83
84
|
var _a2, _b;
|
|
@@ -202,11 +203,12 @@ const listener = ({
|
|
|
202
203
|
}, listenAfterMs);
|
|
203
204
|
};
|
|
204
205
|
};
|
|
206
|
+
const LISTENER_SCRIPT = listener.toString();
|
|
205
207
|
const getScriptListener = ({
|
|
206
208
|
SRC_ELEMEMENTS_QUERY = [],
|
|
207
209
|
listenAfterMs = 2500
|
|
208
210
|
} = {}) => {
|
|
209
|
-
return `<script defer>(${
|
|
211
|
+
return `<script defer>(${LISTENER_SCRIPT})({ SRC_ELEMEMENTS_QUERY: ${JSON.stringify(SRC_ELEMEMENTS_QUERY)}, listenAfterMs: ${listenAfterMs} })<\/script>`;
|
|
210
212
|
};
|
|
211
213
|
exports.getScriptListener = getScriptListener;
|
|
212
214
|
exports.listener = listener;
|
package/dist/listener.es.js
CHANGED
|
@@ -76,6 +76,7 @@ const listener = ({
|
|
|
76
76
|
const script = document == null ? void 0 : document.createElement("script");
|
|
77
77
|
(_a = Object.keys(attrs)) == null ? void 0 : _a.forEach((attrKey) => script == null ? void 0 : script.setAttribute(attrKey, attrs[attrKey]));
|
|
78
78
|
script.id = id;
|
|
79
|
+
script.type = "module";
|
|
79
80
|
if (content) script == null ? void 0 : script.insertAdjacentHTML("beforeend", content);
|
|
80
81
|
return new Promise((resolve, reject) => {
|
|
81
82
|
var _a2, _b;
|
|
@@ -200,11 +201,12 @@ const listener = ({
|
|
|
200
201
|
}, listenAfterMs);
|
|
201
202
|
};
|
|
202
203
|
};
|
|
204
|
+
const LISTENER_SCRIPT = listener.toString();
|
|
203
205
|
const getScriptListener = ({
|
|
204
206
|
SRC_ELEMEMENTS_QUERY = [],
|
|
205
207
|
listenAfterMs = 2500
|
|
206
208
|
} = {}) => {
|
|
207
|
-
return `<script defer>(${
|
|
209
|
+
return `<script defer>(${LISTENER_SCRIPT})({ SRC_ELEMEMENTS_QUERY: ${JSON.stringify(SRC_ELEMEMENTS_QUERY)}, listenAfterMs: ${listenAfterMs} })<\/script>`;
|
|
208
210
|
};
|
|
209
211
|
export {
|
|
210
212
|
getScriptListener,
|
package/package.json
CHANGED
package/src/constants.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
const listener = ({
|
|
2
|
+
SRC_ELEMEMENTS_QUERY = [],
|
|
3
|
+
listenAfterMs = 2_500,
|
|
4
|
+
} = {}) => {
|
|
5
|
+
const EVENTS_PREVENT_DEFAULT_MANDATORY = [
|
|
6
|
+
'submit'
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
const EVENTS = [
|
|
10
|
+
'mouseover',
|
|
11
|
+
'click',
|
|
12
|
+
'submit',
|
|
13
|
+
'input',
|
|
14
|
+
'blur',
|
|
15
|
+
'change',
|
|
16
|
+
'focus',
|
|
17
|
+
'invalid',
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const addListener = ({ srcElement, eventName, listeners, afterExecuteListeners }) => {
|
|
21
|
+
srcElement?.addEventListener(eventName, (e) => {
|
|
22
|
+
executeListeners({ e, srcElement, listeners, afterExecuteListeners })
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const executeListeners = async ({ e, srcElement, listeners, afterExecuteListeners }) => {
|
|
27
|
+
await Promise.all(
|
|
28
|
+
(listeners ?? []).map((listener) => {
|
|
29
|
+
if (listener) return listener({ e, srcElement })
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if (afterExecuteListeners) await afterExecuteListeners()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const getListenerFromScript = ({ script, eventName }) => {
|
|
37
|
+
if (!script) return null
|
|
38
|
+
if (script[eventName]) return script[eventName]
|
|
39
|
+
const prev = Object.keys(script)?.find((key) => script[key][eventName])
|
|
40
|
+
if (!prev) return null
|
|
41
|
+
return script[prev][eventName]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const fetchListeners = async ({ srcElement, eventName, e }) => {
|
|
45
|
+
if (!srcElement?.getAttribute) return
|
|
46
|
+
|
|
47
|
+
const scriptNames = srcElement?.getAttribute(`on-${eventName}`)
|
|
48
|
+
if (!scriptNames) return
|
|
49
|
+
|
|
50
|
+
if (e && scriptNames && EVENTS_PREVENT_DEFAULT_MANDATORY.includes(eventName)) e.preventDefault()
|
|
51
|
+
|
|
52
|
+
const scripts = await Promise.all(
|
|
53
|
+
scriptNames?.split(',')?.map((scriptName) => {
|
|
54
|
+
const scriptToImport = `/${scriptName?.trim()}.js`
|
|
55
|
+
return importScriptDynamically({ path: scriptToImport })
|
|
56
|
+
})
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const listeners = scripts?.map((script) => getListenerFromScript({ script, eventName }))
|
|
60
|
+
|
|
61
|
+
return listeners
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const addScripts = () => {
|
|
65
|
+
const scriptsToLoad = [...document.querySelectorAll('script[data-script-to-load]')]
|
|
66
|
+
return Promise.all(
|
|
67
|
+
scriptsToLoad?.map((scriptToLoad) => {
|
|
68
|
+
const id = scriptToLoad?.getAttribute('data-script-to-load')
|
|
69
|
+
scriptToLoad.removeAttribute('data-script-to-load')
|
|
70
|
+
|
|
71
|
+
const attrs = scriptToLoad?.getAttributeNames()?.reduce((acc, attrName) => {
|
|
72
|
+
const attrValue = scriptToLoad.getAttribute(attrName)
|
|
73
|
+
if (attrValue !== 'text/script-to-load') acc[attrName] = attrValue
|
|
74
|
+
return acc
|
|
75
|
+
}, {})
|
|
76
|
+
|
|
77
|
+
const content = scriptToLoad?.textContent
|
|
78
|
+
|
|
79
|
+
scriptToLoad?.remove()
|
|
80
|
+
|
|
81
|
+
return loadScript({ id, attrs, content }).catch((err) => {
|
|
82
|
+
console.error(err)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const loadScript = ({ id, attrs, content }) => {
|
|
89
|
+
const script = document?.createElement('script')
|
|
90
|
+
|
|
91
|
+
Object.keys(attrs)?.forEach((attrKey) => script?.setAttribute(attrKey, attrs[attrKey]))
|
|
92
|
+
script.id = id
|
|
93
|
+
script.type = 'module'
|
|
94
|
+
|
|
95
|
+
if (content) script?.insertAdjacentHTML('beforeend', content)
|
|
96
|
+
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (!attrs.src) {
|
|
99
|
+
resolve()
|
|
100
|
+
document?.body?.insertAdjacentElement('beforeend', script)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
script.onload = script.onreadystatechange = function () {
|
|
105
|
+
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
|
|
106
|
+
resolve()
|
|
107
|
+
script.onload = script.onreadystatechange = null
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
script.onerror = () => {
|
|
112
|
+
console.error('script failed to load')
|
|
113
|
+
reject(new Error(`Failed to load script with src ${script.src}`))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
document?.body?.insertAdjacentElement('beforeend', script)
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// FIX: return import() to prevent __vitePreload to take action
|
|
121
|
+
const importScriptDynamically = ({ path }) => {
|
|
122
|
+
return import(path)?.catch((err) => { })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// load
|
|
126
|
+
const fireLoadListener = () => {
|
|
127
|
+
const eventName = 'load'
|
|
128
|
+
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
129
|
+
|
|
130
|
+
srcElements?.forEach(async (srcElement) => {
|
|
131
|
+
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
132
|
+
executeListeners({ e: null, srcElement, listeners })
|
|
133
|
+
|
|
134
|
+
srcElement?.removeAttribute(`on-${eventName}`)
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// invalid
|
|
139
|
+
const fireInvalidListener = () => {
|
|
140
|
+
const eventName = 'invalid'
|
|
141
|
+
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
142
|
+
|
|
143
|
+
srcElements?.forEach(async (srcElement) => {
|
|
144
|
+
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
145
|
+
addListener({ srcElement, eventName, listeners })
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// blur
|
|
150
|
+
const fireBlurListener = () => {
|
|
151
|
+
const eventName = 'blur'
|
|
152
|
+
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
153
|
+
|
|
154
|
+
srcElements?.forEach(async (srcElement) => {
|
|
155
|
+
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
156
|
+
addListener({ srcElement, eventName, listeners })
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// focus
|
|
161
|
+
const fireFocusListener = () => {
|
|
162
|
+
const eventName = 'focus'
|
|
163
|
+
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
164
|
+
|
|
165
|
+
srcElements?.forEach(async (srcElement) => {
|
|
166
|
+
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
167
|
+
addListener({ srcElement, eventName, listeners })
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// observers
|
|
172
|
+
const fireObserverListeners = () => {
|
|
173
|
+
const srcElements = [...document.querySelectorAll('[on-observe]')]
|
|
174
|
+
|
|
175
|
+
const uniqueScriptNames = [...srcElements?.reduce((acc, srcElement) => {
|
|
176
|
+
const attribute = srcElement?.getAttribute('on-observe')
|
|
177
|
+
if (attribute === 'undefined') return acc
|
|
178
|
+
|
|
179
|
+
const scriptNames = attribute?.split(',')
|
|
180
|
+
scriptNames?.forEach((scriptName) => acc?.set(scriptName, 1))
|
|
181
|
+
|
|
182
|
+
return acc
|
|
183
|
+
}, new Map())?.keys()]
|
|
184
|
+
|
|
185
|
+
uniqueScriptNames?.forEach(async (scriptName) => {
|
|
186
|
+
const observedSrcElements = document.querySelectorAll(`[on-observe*="${scriptName}"]`)
|
|
187
|
+
|
|
188
|
+
const script = await importScriptDynamically({ path: `/${scriptName?.trim()}.js` })
|
|
189
|
+
const listener = getListenerFromScript({ script, eventName: 'observe' })
|
|
190
|
+
if (!listener) return
|
|
191
|
+
|
|
192
|
+
const observer = new IntersectionObserver((entries) => {
|
|
193
|
+
entries.forEach((entry) => listener({ entry, observer }))
|
|
194
|
+
}, {
|
|
195
|
+
threshold: .5,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
observedSrcElements?.forEach((observerSrcElement) => {
|
|
199
|
+
observer.observe(observerSrcElement)
|
|
200
|
+
|
|
201
|
+
const observerAttr = observerSrcElement?.getAttribute('on-observe')
|
|
202
|
+
const updatedObserverAttr = observerAttr?.replaceAll(scriptName + ', ', '')?.replaceAll(', ' + scriptName, '')?.replaceAll(scriptName, '')
|
|
203
|
+
|
|
204
|
+
if (!updatedObserverAttr) observerSrcElement.removeAttribute('on-observe')
|
|
205
|
+
else observerSrcElement.setAttribute('on-observe', updatedObserverAttr)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const getSrcElement = ({ srcElement, eventName }) => {
|
|
211
|
+
if (!srcElement?.hasAttribute) return srcElement
|
|
212
|
+
const attribute = `on-${eventName}`
|
|
213
|
+
const hasScriptName = srcElement?.hasAttribute(attribute)
|
|
214
|
+
if (hasScriptName) return srcElement
|
|
215
|
+
|
|
216
|
+
const query = `:is(${['a', 'button', ...SRC_ELEMEMENTS_QUERY].join(',')})[${attribute}]`
|
|
217
|
+
const closestButton = srcElement?.closest(query)
|
|
218
|
+
if (closestButton) return closestButton
|
|
219
|
+
|
|
220
|
+
return srcElement
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const cloneSrcElement = ({ srcElement }) => {
|
|
224
|
+
const newSrcElement = document.createElement(srcElement?.tagName)
|
|
225
|
+
|
|
226
|
+
Array.from(srcElement?.attributes).forEach((attr) => {
|
|
227
|
+
if (attr.name.startsWith('on-')) return
|
|
228
|
+
newSrcElement.setAttribute(attr.name, attr.value)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
return newSrcElement
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const isAnchorBeingClicked = ({ srcElement, eventName }) => {
|
|
235
|
+
return srcElement?.tagName === 'A' && eventName === 'click'
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const clickDefaultAnchor = ({ srcElement }) => {
|
|
239
|
+
const newAnchor = cloneSrcElement({ srcElement })
|
|
240
|
+
newAnchor.click()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const fireListeners = () => {
|
|
244
|
+
EVENTS.forEach((eventName) => {
|
|
245
|
+
document.body[`on${eventName}`] = async (e) => {
|
|
246
|
+
const srcElement = getSrcElement({ srcElement: e?.srcElement, eventName })
|
|
247
|
+
const isAnchorClicked = isAnchorBeingClicked({ srcElement, eventName })
|
|
248
|
+
|
|
249
|
+
if (isAnchorClicked) e.preventDefault()
|
|
250
|
+
|
|
251
|
+
const listeners = await fetchListeners({ srcElement, eventName, e })
|
|
252
|
+
const afterExecuteListeners = isAnchorClicked
|
|
253
|
+
? () => { clickDefaultAnchor({ srcElement }) }
|
|
254
|
+
: null
|
|
255
|
+
|
|
256
|
+
executeListeners({ e, srcElement, listeners, afterExecuteListeners })
|
|
257
|
+
addListener({ srcElement, eventName, listeners, afterExecuteListeners })
|
|
258
|
+
|
|
259
|
+
if (srcElement?.removeAttribute) srcElement.removeAttribute(`on-${eventName}`)
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const saloeListenEvent = new CustomEvent('saloeListen', {
|
|
264
|
+
detail: { message: 'This is a custom eventName!' }
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
document.body.addEventListener('saloeListen', async (e) => {
|
|
268
|
+
await addScripts()
|
|
269
|
+
fireLoadListener()
|
|
270
|
+
fireObserverListeners()
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
document.body.saloeListen = function () {
|
|
274
|
+
document.body.dispatchEvent(saloeListenEvent)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
fireListeners()
|
|
280
|
+
|
|
281
|
+
window.onload = () => {
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
document.body.saloeListen()
|
|
284
|
+
}, listenAfterMs)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const LISTENER_SCRIPT = listener.toString()
|
|
289
|
+
|
|
290
|
+
export {
|
|
291
|
+
listener,
|
|
292
|
+
LISTENER_SCRIPT,
|
|
293
|
+
}
|
package/src/listener.js
CHANGED
|
@@ -1,294 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
SRC_ELEMEMENTS_QUERY = [],
|
|
3
|
-
listenAfterMs = 2_500,
|
|
4
|
-
} = {}) => {
|
|
5
|
-
const EVENTS_PREVENT_DEFAULT_MANDATORY = [
|
|
6
|
-
'submit'
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
const EVENTS = [
|
|
10
|
-
'mouseover',
|
|
11
|
-
'click',
|
|
12
|
-
'submit',
|
|
13
|
-
'input',
|
|
14
|
-
'blur',
|
|
15
|
-
'change',
|
|
16
|
-
'focus',
|
|
17
|
-
'invalid',
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
const addListener = ({ srcElement, eventName, listeners, afterExecuteListeners }) => {
|
|
21
|
-
srcElement?.addEventListener(eventName, (e) => {
|
|
22
|
-
executeListeners({ e, srcElement, listeners, afterExecuteListeners })
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const executeListeners = async ({ e, srcElement, listeners, afterExecuteListeners }) => {
|
|
27
|
-
await Promise.all(
|
|
28
|
-
(listeners ?? []).map((listener) => {
|
|
29
|
-
if (listener) return listener({ e, srcElement })
|
|
30
|
-
})
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
if (afterExecuteListeners) await afterExecuteListeners()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const getListenerFromScript = ({ script, eventName }) => {
|
|
37
|
-
if (!script) return null
|
|
38
|
-
if (script[eventName]) return script[eventName]
|
|
39
|
-
const prev = Object.keys(script)?.find((key) => script[key][eventName])
|
|
40
|
-
if (!prev) return null
|
|
41
|
-
return script[prev][eventName]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const fetchListeners = async ({ srcElement, eventName, e }) => {
|
|
45
|
-
if (!srcElement?.getAttribute) return
|
|
46
|
-
|
|
47
|
-
const scriptNames = srcElement?.getAttribute(`on-${eventName}`)
|
|
48
|
-
if (!scriptNames) return
|
|
49
|
-
|
|
50
|
-
if (e && scriptNames && EVENTS_PREVENT_DEFAULT_MANDATORY.includes(eventName)) e.preventDefault()
|
|
51
|
-
|
|
52
|
-
const scripts = await Promise.all(
|
|
53
|
-
scriptNames?.split(',')?.map((scriptName) => {
|
|
54
|
-
const scriptToImport = `/${scriptName?.trim()}.js`
|
|
55
|
-
return importScriptDynamically({ path: scriptToImport })
|
|
56
|
-
})
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
const listeners = scripts?.map((script) => getListenerFromScript({ script, eventName }))
|
|
60
|
-
|
|
61
|
-
return listeners
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const addScripts = () => {
|
|
65
|
-
const scriptsToLoad = [...document.querySelectorAll('script[data-script-to-load]')]
|
|
66
|
-
return Promise.all(
|
|
67
|
-
scriptsToLoad?.map((scriptToLoad) => {
|
|
68
|
-
const id = scriptToLoad?.getAttribute('data-script-to-load')
|
|
69
|
-
scriptToLoad.removeAttribute('data-script-to-load')
|
|
70
|
-
|
|
71
|
-
const attrs = scriptToLoad?.getAttributeNames()?.reduce((acc, attrName) => {
|
|
72
|
-
const attrValue = scriptToLoad.getAttribute(attrName)
|
|
73
|
-
if (attrValue !== 'text/script-to-load') acc[attrName] = attrValue
|
|
74
|
-
return acc
|
|
75
|
-
}, {})
|
|
76
|
-
|
|
77
|
-
const content = scriptToLoad?.textContent
|
|
78
|
-
|
|
79
|
-
scriptToLoad?.remove()
|
|
80
|
-
|
|
81
|
-
return loadScript({ id, attrs, content }).catch((err) => {
|
|
82
|
-
console.error(err)
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const loadScript = ({ id, attrs, content }) => {
|
|
89
|
-
const script = document?.createElement('script')
|
|
90
|
-
|
|
91
|
-
Object.keys(attrs)?.forEach((attrKey) => script?.setAttribute(attrKey, attrs[attrKey]))
|
|
92
|
-
script.id = id
|
|
93
|
-
|
|
94
|
-
if (content) script?.insertAdjacentHTML('beforeend', content)
|
|
95
|
-
|
|
96
|
-
return new Promise((resolve, reject) => {
|
|
97
|
-
if (!attrs.src) {
|
|
98
|
-
resolve()
|
|
99
|
-
document?.body?.insertAdjacentElement('beforeend', script)
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
script.onload = script.onreadystatechange = function () {
|
|
104
|
-
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
|
|
105
|
-
resolve()
|
|
106
|
-
script.onload = script.onreadystatechange = null
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
script.onerror = () => {
|
|
111
|
-
console.error('script failed to load')
|
|
112
|
-
reject(new Error(`Failed to load script with src ${script.src}`))
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
document?.body?.insertAdjacentElement('beforeend', script)
|
|
116
|
-
})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// FIX: return import() to prevent __vitePreload to take action
|
|
120
|
-
const importScriptDynamically = ({ path }) => {
|
|
121
|
-
return import(path)?.catch((err) => { })
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// load
|
|
125
|
-
const fireLoadListener = () => {
|
|
126
|
-
const eventName = 'load'
|
|
127
|
-
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
128
|
-
|
|
129
|
-
srcElements?.forEach(async (srcElement) => {
|
|
130
|
-
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
131
|
-
executeListeners({ e: null, srcElement, listeners })
|
|
132
|
-
|
|
133
|
-
srcElement?.removeAttribute(`on-${eventName}`)
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// invalid
|
|
138
|
-
const fireInvalidListener = () => {
|
|
139
|
-
const eventName = 'invalid'
|
|
140
|
-
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
141
|
-
|
|
142
|
-
srcElements?.forEach(async (srcElement) => {
|
|
143
|
-
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
144
|
-
addListener({ srcElement, eventName, listeners })
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// blur
|
|
149
|
-
const fireBlurListener = () => {
|
|
150
|
-
const eventName = 'blur'
|
|
151
|
-
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
1
|
+
import { listener, LISTENER_SCRIPT } from './constants'
|
|
152
2
|
|
|
153
|
-
srcElements?.forEach(async (srcElement) => {
|
|
154
|
-
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
155
|
-
addListener({ srcElement, eventName, listeners })
|
|
156
|
-
})
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// focus
|
|
160
|
-
const fireFocusListener = () => {
|
|
161
|
-
const eventName = 'focus'
|
|
162
|
-
const srcElements = document?.querySelectorAll(`[on-${eventName}]`)
|
|
163
|
-
|
|
164
|
-
srcElements?.forEach(async (srcElement) => {
|
|
165
|
-
const listeners = await fetchListeners({ srcElement, eventName, e: null })
|
|
166
|
-
addListener({ srcElement, eventName, listeners })
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// observers
|
|
171
|
-
const fireObserverListeners = () => {
|
|
172
|
-
const srcElements = [...document.querySelectorAll('[on-observe]')]
|
|
173
|
-
|
|
174
|
-
const uniqueScriptNames = [...srcElements?.reduce((acc, srcElement) => {
|
|
175
|
-
const attribute = srcElement?.getAttribute('on-observe')
|
|
176
|
-
if (attribute === 'undefined') return acc
|
|
177
|
-
|
|
178
|
-
const scriptNames = attribute?.split(',')
|
|
179
|
-
scriptNames?.forEach((scriptName) => acc?.set(scriptName, 1))
|
|
180
|
-
|
|
181
|
-
return acc
|
|
182
|
-
}, new Map())?.keys()]
|
|
183
|
-
|
|
184
|
-
uniqueScriptNames?.forEach(async (scriptName) => {
|
|
185
|
-
const observedSrcElements = document.querySelectorAll(`[on-observe*="${scriptName}"]`)
|
|
186
|
-
|
|
187
|
-
const script = await importScriptDynamically({ path: `/${scriptName?.trim()}.js` })
|
|
188
|
-
const listener = getListenerFromScript({ script, eventName: 'observe' })
|
|
189
|
-
if (!listener) return
|
|
190
|
-
|
|
191
|
-
const observer = new IntersectionObserver((entries) => {
|
|
192
|
-
entries.forEach((entry) => listener({ entry, observer }))
|
|
193
|
-
}, {
|
|
194
|
-
threshold: .5,
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
observedSrcElements?.forEach((observerSrcElement) => {
|
|
198
|
-
observer.observe(observerSrcElement)
|
|
199
|
-
|
|
200
|
-
const observerAttr = observerSrcElement?.getAttribute('on-observe')
|
|
201
|
-
const updatedObserverAttr = observerAttr?.replaceAll(scriptName + ', ', '')?.replaceAll(', ' + scriptName, '')?.replaceAll(scriptName, '')
|
|
202
|
-
|
|
203
|
-
if (!updatedObserverAttr) observerSrcElement.removeAttribute('on-observe')
|
|
204
|
-
else observerSrcElement.setAttribute('on-observe', updatedObserverAttr)
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const getSrcElement = ({ srcElement, eventName }) => {
|
|
210
|
-
if (!srcElement?.hasAttribute) return srcElement
|
|
211
|
-
const attribute = `on-${eventName}`
|
|
212
|
-
const hasScriptName = srcElement?.hasAttribute(attribute)
|
|
213
|
-
if (hasScriptName) return srcElement
|
|
214
|
-
|
|
215
|
-
const query = `:is(${['a', 'button', ...SRC_ELEMEMENTS_QUERY].join(',')})[${attribute}]`
|
|
216
|
-
const closestButton = srcElement?.closest(query)
|
|
217
|
-
if (closestButton) return closestButton
|
|
218
|
-
|
|
219
|
-
return srcElement
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const cloneSrcElement = ({ srcElement }) => {
|
|
223
|
-
const newSrcElement = document.createElement(srcElement?.tagName)
|
|
224
|
-
|
|
225
|
-
Array.from(srcElement?.attributes).forEach((attr) => {
|
|
226
|
-
if (attr.name.startsWith('on-')) return
|
|
227
|
-
newSrcElement.setAttribute(attr.name, attr.value)
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
return newSrcElement
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const isAnchorBeingClicked = ({ srcElement, eventName }) => {
|
|
234
|
-
return srcElement?.tagName === 'A' && eventName === 'click'
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const clickDefaultAnchor = ({ srcElement }) => {
|
|
238
|
-
const newAnchor = cloneSrcElement({ srcElement })
|
|
239
|
-
newAnchor.click()
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const fireListeners = () => {
|
|
243
|
-
EVENTS.forEach((eventName) => {
|
|
244
|
-
document.body[`on${eventName}`] = async (e) => {
|
|
245
|
-
const srcElement = getSrcElement({ srcElement: e?.srcElement, eventName })
|
|
246
|
-
const isAnchorClicked = isAnchorBeingClicked({ srcElement, eventName })
|
|
247
|
-
|
|
248
|
-
if (isAnchorClicked) e.preventDefault()
|
|
249
|
-
|
|
250
|
-
const listeners = await fetchListeners({ srcElement, eventName, e })
|
|
251
|
-
const afterExecuteListeners = isAnchorClicked
|
|
252
|
-
? () => { clickDefaultAnchor({ srcElement }) }
|
|
253
|
-
: null
|
|
254
|
-
|
|
255
|
-
executeListeners({ e, srcElement, listeners, afterExecuteListeners })
|
|
256
|
-
addListener({ srcElement, eventName, listeners, afterExecuteListeners })
|
|
257
|
-
|
|
258
|
-
if (srcElement?.removeAttribute) srcElement.removeAttribute(`on-${eventName}`)
|
|
259
|
-
}
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
const saloeListenEvent = new CustomEvent('saloeListen', {
|
|
263
|
-
detail: { message: 'This is a custom eventName!' }
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
document.body.addEventListener('saloeListen', async (e) => {
|
|
267
|
-
await addScripts()
|
|
268
|
-
fireLoadListener()
|
|
269
|
-
fireObserverListeners()
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
document.body.saloeListen = function () {
|
|
273
|
-
document.body.dispatchEvent(saloeListenEvent)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
fireListeners()
|
|
279
|
-
|
|
280
|
-
window.onload = () => {
|
|
281
|
-
setTimeout(() => {
|
|
282
|
-
document.body.saloeListen()
|
|
283
|
-
}, listenAfterMs)
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
3
|
|
|
287
4
|
const getScriptListener = ({
|
|
288
5
|
SRC_ELEMEMENTS_QUERY = [],
|
|
289
6
|
listenAfterMs = 2_500
|
|
290
7
|
} = {}) => {
|
|
291
|
-
return `<script defer>(${
|
|
8
|
+
return `<script defer>(${LISTENER_SCRIPT})({ SRC_ELEMEMENTS_QUERY: ${JSON.stringify(SRC_ELEMEMENTS_QUERY)}, listenAfterMs: ${listenAfterMs} })</script>`
|
|
292
9
|
}
|
|
293
10
|
|
|
294
11
|
export {
|