htmx.org 2.0.0 → 2.0.2
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/README.md +3 -4
- package/dist/ext/README.md +9 -0
- package/dist/ext/ajax-header.js +11 -0
- package/dist/ext/alpine-morph.js +20 -0
- package/dist/ext/class-tools.js +97 -0
- package/dist/ext/client-side-templates.js +100 -0
- package/dist/ext/debug.js +15 -0
- package/dist/ext/disable-element.js +20 -0
- package/dist/ext/event-header.js +41 -0
- package/dist/ext/head-support.js +146 -0
- package/dist/ext/include-vals.js +28 -0
- package/dist/ext/json-enc.js +16 -0
- package/dist/ext/loading-states.js +189 -0
- package/dist/ext/method-override.js +15 -0
- package/dist/ext/morphdom-swap.js +21 -0
- package/dist/ext/multi-swap.js +50 -0
- package/dist/ext/path-deps.js +63 -0
- package/dist/ext/path-params.js +15 -0
- package/dist/ext/preload.js +151 -0
- package/dist/ext/rails-method.js +14 -0
- package/dist/ext/remove-me.js +31 -0
- package/dist/ext/response-targets.js +135 -0
- package/dist/ext/restored.js +19 -0
- package/dist/ext/sse.js +374 -0
- package/dist/ext/ws.js +481 -0
- package/dist/htmx.amd.js +47 -22
- package/dist/htmx.cjs.js +47 -22
- package/dist/htmx.esm.d.ts +205 -0
- package/dist/htmx.esm.js +47 -22
- package/dist/htmx.js +47 -22
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.gz +0 -0
- package/editors/jetbrains/htmx.web-types.json +1 -1
- package/package.json +11 -8
- package/CHANGELOG.md +0 -485
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
;(function () {
|
|
2
|
+
|
|
3
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
4
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
5
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let loadingStatesUndoQueue = []
|
|
9
|
+
|
|
10
|
+
function loadingStateContainer(target) {
|
|
11
|
+
return htmx.closest(target, '[data-loading-states]') || document.body
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function mayProcessUndoCallback(target, callback) {
|
|
15
|
+
if (document.body.contains(target)) {
|
|
16
|
+
callback()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function mayProcessLoadingStateByPath(elt, requestPath) {
|
|
21
|
+
const pathElt = htmx.closest(elt, '[data-loading-path]')
|
|
22
|
+
if (!pathElt) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return pathElt.getAttribute('data-loading-path') === requestPath
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function queueLoadingState(sourceElt, targetElt, doCallback, undoCallback) {
|
|
30
|
+
const delayElt = htmx.closest(sourceElt, '[data-loading-delay]')
|
|
31
|
+
if (delayElt) {
|
|
32
|
+
const delayInMilliseconds =
|
|
33
|
+
delayElt.getAttribute('data-loading-delay') || 200
|
|
34
|
+
const timeout = setTimeout(function () {
|
|
35
|
+
doCallback()
|
|
36
|
+
|
|
37
|
+
loadingStatesUndoQueue.push(function () {
|
|
38
|
+
mayProcessUndoCallback(targetElt, undoCallback)
|
|
39
|
+
})
|
|
40
|
+
}, delayInMilliseconds)
|
|
41
|
+
|
|
42
|
+
loadingStatesUndoQueue.push(function () {
|
|
43
|
+
mayProcessUndoCallback(targetElt, function () { clearTimeout(timeout) })
|
|
44
|
+
})
|
|
45
|
+
} else {
|
|
46
|
+
doCallback()
|
|
47
|
+
loadingStatesUndoQueue.push(function () {
|
|
48
|
+
mayProcessUndoCallback(targetElt, undoCallback)
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getLoadingStateElts(loadingScope, type, path) {
|
|
54
|
+
return Array.from(htmx.findAll(loadingScope, "[" + type + "]")).filter(
|
|
55
|
+
function (elt) { return mayProcessLoadingStateByPath(elt, path) }
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getLoadingTarget(elt) {
|
|
60
|
+
if (elt.getAttribute('data-loading-target')) {
|
|
61
|
+
return Array.from(
|
|
62
|
+
htmx.findAll(elt.getAttribute('data-loading-target'))
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
return [elt]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
htmx.defineExtension('loading-states', {
|
|
69
|
+
onEvent: function (name, evt) {
|
|
70
|
+
if (name === 'htmx:beforeRequest') {
|
|
71
|
+
const container = loadingStateContainer(evt.target)
|
|
72
|
+
|
|
73
|
+
const loadingStateTypes = [
|
|
74
|
+
'data-loading',
|
|
75
|
+
'data-loading-class',
|
|
76
|
+
'data-loading-class-remove',
|
|
77
|
+
'data-loading-disable',
|
|
78
|
+
'data-loading-aria-busy',
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
let loadingStateEltsByType = {}
|
|
82
|
+
|
|
83
|
+
loadingStateTypes.forEach(function (type) {
|
|
84
|
+
loadingStateEltsByType[type] = getLoadingStateElts(
|
|
85
|
+
container,
|
|
86
|
+
type,
|
|
87
|
+
evt.detail.pathInfo.requestPath
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
loadingStateEltsByType['data-loading'].forEach(function (sourceElt) {
|
|
92
|
+
getLoadingTarget(sourceElt).forEach(function (targetElt) {
|
|
93
|
+
queueLoadingState(
|
|
94
|
+
sourceElt,
|
|
95
|
+
targetElt,
|
|
96
|
+
function () {
|
|
97
|
+
targetElt.style.display =
|
|
98
|
+
sourceElt.getAttribute('data-loading') ||
|
|
99
|
+
'inline-block' },
|
|
100
|
+
function () { targetElt.style.display = 'none' }
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
loadingStateEltsByType['data-loading-class'].forEach(
|
|
106
|
+
function (sourceElt) {
|
|
107
|
+
const classNames = sourceElt
|
|
108
|
+
.getAttribute('data-loading-class')
|
|
109
|
+
.split(' ')
|
|
110
|
+
|
|
111
|
+
getLoadingTarget(sourceElt).forEach(function (targetElt) {
|
|
112
|
+
queueLoadingState(
|
|
113
|
+
sourceElt,
|
|
114
|
+
targetElt,
|
|
115
|
+
function () {
|
|
116
|
+
classNames.forEach(function (className) {
|
|
117
|
+
targetElt.classList.add(className)
|
|
118
|
+
})
|
|
119
|
+
},
|
|
120
|
+
function() {
|
|
121
|
+
classNames.forEach(function (className) {
|
|
122
|
+
targetElt.classList.remove(className)
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
loadingStateEltsByType['data-loading-class-remove'].forEach(
|
|
131
|
+
function (sourceElt) {
|
|
132
|
+
const classNames = sourceElt
|
|
133
|
+
.getAttribute('data-loading-class-remove')
|
|
134
|
+
.split(' ')
|
|
135
|
+
|
|
136
|
+
getLoadingTarget(sourceElt).forEach(function (targetElt) {
|
|
137
|
+
queueLoadingState(
|
|
138
|
+
sourceElt,
|
|
139
|
+
targetElt,
|
|
140
|
+
function () {
|
|
141
|
+
classNames.forEach(function (className) {
|
|
142
|
+
targetElt.classList.remove(className)
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
function() {
|
|
146
|
+
classNames.forEach(function (className) {
|
|
147
|
+
targetElt.classList.add(className)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
loadingStateEltsByType['data-loading-disable'].forEach(
|
|
156
|
+
function (sourceElt) {
|
|
157
|
+
getLoadingTarget(sourceElt).forEach(function (targetElt) {
|
|
158
|
+
queueLoadingState(
|
|
159
|
+
sourceElt,
|
|
160
|
+
targetElt,
|
|
161
|
+
function() { targetElt.disabled = true },
|
|
162
|
+
function() { targetElt.disabled = false }
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
loadingStateEltsByType['data-loading-aria-busy'].forEach(
|
|
169
|
+
function (sourceElt) {
|
|
170
|
+
getLoadingTarget(sourceElt).forEach(function (targetElt) {
|
|
171
|
+
queueLoadingState(
|
|
172
|
+
sourceElt,
|
|
173
|
+
targetElt,
|
|
174
|
+
function () { targetElt.setAttribute("aria-busy", "true") },
|
|
175
|
+
function () { targetElt.removeAttribute("aria-busy") }
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (name === 'htmx:beforeOnLoad') {
|
|
183
|
+
while (loadingStatesUndoQueue.length > 0) {
|
|
184
|
+
loadingStatesUndoQueue.shift()()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
})()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
2
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
3
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
4
|
+
}
|
|
5
|
+
htmx.defineExtension('method-override', {
|
|
6
|
+
onEvent: function (name, evt) {
|
|
7
|
+
if (name === "htmx:configRequest") {
|
|
8
|
+
var method = evt.detail.verb;
|
|
9
|
+
if (method !== "get" || method !== "post") {
|
|
10
|
+
evt.detail.headers['X-HTTP-Method-Override'] = method.toUpperCase();
|
|
11
|
+
evt.detail.verb = "post";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
2
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
3
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
4
|
+
}
|
|
5
|
+
htmx.defineExtension('morphdom-swap', {
|
|
6
|
+
isInlineSwap: function(swapStyle) {
|
|
7
|
+
return swapStyle === 'morphdom';
|
|
8
|
+
},
|
|
9
|
+
handleSwap: function (swapStyle, target, fragment) {
|
|
10
|
+
if (swapStyle === 'morphdom') {
|
|
11
|
+
if (fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
12
|
+
// IE11 doesn't support DocumentFragment.firstElementChild
|
|
13
|
+
morphdom(target, fragment.firstElementChild || fragment.firstChild);
|
|
14
|
+
return [target];
|
|
15
|
+
} else {
|
|
16
|
+
morphdom(target, fragment.outerHTML);
|
|
17
|
+
return [target];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
|
|
3
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
4
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
5
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** @type {import("../htmx").HtmxInternalApi} */
|
|
9
|
+
var api;
|
|
10
|
+
|
|
11
|
+
htmx.defineExtension('multi-swap', {
|
|
12
|
+
init: function (apiRef) {
|
|
13
|
+
api = apiRef;
|
|
14
|
+
},
|
|
15
|
+
isInlineSwap: function (swapStyle) {
|
|
16
|
+
return swapStyle.indexOf('multi:') === 0;
|
|
17
|
+
},
|
|
18
|
+
handleSwap: function (swapStyle, target, fragment, settleInfo) {
|
|
19
|
+
if (swapStyle.indexOf('multi:') === 0) {
|
|
20
|
+
var selectorToSwapStyle = {};
|
|
21
|
+
var elements = swapStyle.replace(/^multi\s*:\s*/, '').split(/\s*,\s*/);
|
|
22
|
+
|
|
23
|
+
elements.map(function (element) {
|
|
24
|
+
var split = element.split(/\s*:\s*/);
|
|
25
|
+
var elementSelector = split[0];
|
|
26
|
+
var elementSwapStyle = typeof (split[1]) !== "undefined" ? split[1] : "innerHTML";
|
|
27
|
+
|
|
28
|
+
if (elementSelector.charAt(0) !== '#') {
|
|
29
|
+
console.error("HTMX multi-swap: unsupported selector '" + elementSelector + "'. Only ID selectors starting with '#' are supported.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
selectorToSwapStyle[elementSelector] = elementSwapStyle;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
for (var selector in selectorToSwapStyle) {
|
|
37
|
+
var swapStyle = selectorToSwapStyle[selector];
|
|
38
|
+
var elementToSwap = fragment.querySelector(selector);
|
|
39
|
+
if (elementToSwap) {
|
|
40
|
+
api.oobSwap(swapStyle, elementToSwap, settleInfo);
|
|
41
|
+
} else {
|
|
42
|
+
console.warn("HTMX multi-swap: selector '" + selector + "' not found in source content.");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
})();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
(function(undefined){
|
|
2
|
+
'use strict';
|
|
3
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
4
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
5
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
6
|
+
}
|
|
7
|
+
// Save a reference to the global object (window in the browser)
|
|
8
|
+
var _root = this;
|
|
9
|
+
|
|
10
|
+
function dependsOn(pathSpec, url) {
|
|
11
|
+
if (pathSpec === "ignore") {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
var dependencyPath = pathSpec.split("/");
|
|
15
|
+
var urlPath = url.split("/");
|
|
16
|
+
for (var i = 0; i < urlPath.length; i++) {
|
|
17
|
+
var dependencyElement = dependencyPath.shift();
|
|
18
|
+
var pathElement = urlPath[i];
|
|
19
|
+
if (dependencyElement !== pathElement && dependencyElement !== "*") {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (dependencyPath.length === 0 || (dependencyPath.length === 1 && dependencyPath[0] === "")) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function refreshPath(path) {
|
|
30
|
+
var eltsWithDeps = htmx.findAll("[path-deps]");
|
|
31
|
+
for (var i = 0; i < eltsWithDeps.length; i++) {
|
|
32
|
+
var elt = eltsWithDeps[i];
|
|
33
|
+
if (dependsOn(elt.getAttribute('path-deps'), path)) {
|
|
34
|
+
htmx.trigger(elt, "path-deps");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
htmx.defineExtension('path-deps', {
|
|
40
|
+
onEvent: function (name, evt) {
|
|
41
|
+
if (name === "htmx:beforeOnLoad") {
|
|
42
|
+
var config = evt.detail.requestConfig;
|
|
43
|
+
// mutating call
|
|
44
|
+
if (config.verb !== "get" && evt.target.getAttribute('path-deps') !== 'ignore') {
|
|
45
|
+
refreshPath(config.path);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* ********************
|
|
53
|
+
* Expose functionality
|
|
54
|
+
* ********************
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
_root.PathDeps = {
|
|
58
|
+
refresh: function(path) {
|
|
59
|
+
refreshPath(path);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
}).call(this);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
2
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
3
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
4
|
+
}
|
|
5
|
+
htmx.defineExtension('path-params', {
|
|
6
|
+
onEvent: function(name, evt) {
|
|
7
|
+
if (name === "htmx:configRequest") {
|
|
8
|
+
evt.detail.path = evt.detail.path.replace(/{([^}]+)}/g, function (_, param) {
|
|
9
|
+
var val = evt.detail.parameters[param];
|
|
10
|
+
delete evt.detail.parameters[param];
|
|
11
|
+
return val === undefined ? "{" + param + "}" : encodeURIComponent(val);
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
2
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
3
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
4
|
+
}
|
|
5
|
+
// This adds the "preload" extension to htmx. By default, this will
|
|
6
|
+
// preload the targets of any tags with `href` or `hx-get` attributes
|
|
7
|
+
// if they also have a `preload` attribute as well. See documentation
|
|
8
|
+
// for more details
|
|
9
|
+
htmx.defineExtension("preload", {
|
|
10
|
+
|
|
11
|
+
onEvent: function(name, event) {
|
|
12
|
+
|
|
13
|
+
// Only take actions on "htmx:afterProcessNode"
|
|
14
|
+
if (name !== "htmx:afterProcessNode") {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// SOME HELPER FUNCTIONS WE'LL NEED ALONG THE WAY
|
|
19
|
+
|
|
20
|
+
// attr gets the closest non-empty value from the attribute.
|
|
21
|
+
var attr = function(node, property) {
|
|
22
|
+
if (node == undefined) {return undefined;}
|
|
23
|
+
return node.getAttribute(property) || node.getAttribute("data-" + property) || attr(node.parentElement, property)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// load handles the actual HTTP fetch, and uses htmx.ajax in cases where we're
|
|
27
|
+
// preloading an htmx resource (this sends the same HTTP headers as a regular htmx request)
|
|
28
|
+
var load = function(node) {
|
|
29
|
+
|
|
30
|
+
// Called after a successful AJAX request, to mark the
|
|
31
|
+
// content as loaded (and prevent additional AJAX calls.)
|
|
32
|
+
var done = function(html) {
|
|
33
|
+
if (!node.preloadAlways) {
|
|
34
|
+
node.preloadState = "DONE"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (attr(node, "preload-images") == "true") {
|
|
38
|
+
document.createElement("div").innerHTML = html // create and populate a node to load linked resources, too.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return function() {
|
|
43
|
+
|
|
44
|
+
// If this value has already been loaded, then do not try again.
|
|
45
|
+
if (node.preloadState !== "READY") {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Special handling for HX-GET - use built-in htmx.ajax function
|
|
50
|
+
// so that headers match other htmx requests, then set
|
|
51
|
+
// node.preloadState = TRUE so that requests are not duplicated
|
|
52
|
+
// in the future
|
|
53
|
+
var hxGet = node.getAttribute("hx-get") || node.getAttribute("data-hx-get")
|
|
54
|
+
if (hxGet) {
|
|
55
|
+
htmx.ajax("GET", hxGet, {
|
|
56
|
+
source: node,
|
|
57
|
+
handler:function(elt, info) {
|
|
58
|
+
done(info.xhr.responseText);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Otherwise, perform a standard xhr request, then set
|
|
65
|
+
// node.preloadState = TRUE so that requests are not duplicated
|
|
66
|
+
// in the future.
|
|
67
|
+
if (node.getAttribute("href")) {
|
|
68
|
+
var r = new XMLHttpRequest();
|
|
69
|
+
r.open("GET", node.getAttribute("href"));
|
|
70
|
+
r.onload = function() {done(r.responseText);};
|
|
71
|
+
r.send();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// This function processes a specific node and sets up event handlers.
|
|
78
|
+
// We'll search for nodes and use it below.
|
|
79
|
+
var init = function(node) {
|
|
80
|
+
|
|
81
|
+
// If this node DOES NOT include a "GET" transaction, then there's nothing to do here.
|
|
82
|
+
if (node.getAttribute("href") + node.getAttribute("hx-get") + node.getAttribute("data-hx-get") == "") {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Guarantee that we only initialize each node once.
|
|
87
|
+
if (node.preloadState !== undefined) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Get event name from config.
|
|
92
|
+
var on = attr(node, "preload") || "mousedown"
|
|
93
|
+
const always = on.indexOf("always") !== -1
|
|
94
|
+
if (always) {
|
|
95
|
+
on = on.replace('always', '').trim()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// FALL THROUGH to here means we need to add an EventListener
|
|
99
|
+
|
|
100
|
+
// Apply the listener to the node
|
|
101
|
+
node.addEventListener(on, function(evt) {
|
|
102
|
+
if (node.preloadState === "PAUSE") { // Only add one event listener
|
|
103
|
+
node.preloadState = "READY"; // Required for the `load` function to trigger
|
|
104
|
+
|
|
105
|
+
// Special handling for "mouseover" events. Wait 100ms before triggering load.
|
|
106
|
+
if (on === "mouseover") {
|
|
107
|
+
window.setTimeout(load(node), 100);
|
|
108
|
+
} else {
|
|
109
|
+
load(node)() // all other events trigger immediately.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Special handling for certain built-in event handlers
|
|
115
|
+
switch (on) {
|
|
116
|
+
|
|
117
|
+
case "mouseover":
|
|
118
|
+
// Mirror `touchstart` events (fires immediately)
|
|
119
|
+
node.addEventListener("touchstart", load(node));
|
|
120
|
+
|
|
121
|
+
// WHhen the mouse leaves, immediately disable the preload
|
|
122
|
+
node.addEventListener("mouseout", function(evt) {
|
|
123
|
+
if ((evt.target === node) && (node.preloadState === "READY")) {
|
|
124
|
+
node.preloadState = "PAUSE";
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case "mousedown":
|
|
130
|
+
// Mirror `touchstart` events (fires immediately)
|
|
131
|
+
node.addEventListener("touchstart", load(node));
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Mark the node as ready to run.
|
|
136
|
+
node.preloadState = "PAUSE";
|
|
137
|
+
node.preloadAlways = always;
|
|
138
|
+
htmx.trigger(node, "preload:init") // This event can be used to load content immediately.
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Search for all child nodes that have a "preload" attribute
|
|
142
|
+
event.target.querySelectorAll("[preload]").forEach(function(node) {
|
|
143
|
+
|
|
144
|
+
// Initialize the node with the "preload" attribute
|
|
145
|
+
init(node)
|
|
146
|
+
|
|
147
|
+
// Initialize all child elements that are anchors or have `hx-get` (use with care)
|
|
148
|
+
node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init)
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
2
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
3
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
4
|
+
}
|
|
5
|
+
htmx.defineExtension('rails-method', {
|
|
6
|
+
onEvent: function (name, evt) {
|
|
7
|
+
if (name === "configRequest.htmx") {
|
|
8
|
+
var methodOverride = evt.detail.headers['X-HTTP-Method-Override'];
|
|
9
|
+
if (methodOverride) {
|
|
10
|
+
evt.detail.parameters['_method'] = methodOverride;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
(function(){
|
|
2
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
3
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
4
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
5
|
+
}
|
|
6
|
+
function maybeRemoveMe(elt) {
|
|
7
|
+
var timing = elt.getAttribute("remove-me") || elt.getAttribute("data-remove-me");
|
|
8
|
+
if (timing) {
|
|
9
|
+
setTimeout(function () {
|
|
10
|
+
elt.parentElement.removeChild(elt);
|
|
11
|
+
}, htmx.parseInterval(timing));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
htmx.defineExtension('remove-me', {
|
|
16
|
+
onEvent: function (name, evt) {
|
|
17
|
+
if (name === "htmx:afterProcessNode") {
|
|
18
|
+
var elt = evt.detail.elt;
|
|
19
|
+
if (elt.getAttribute) {
|
|
20
|
+
maybeRemoveMe(elt);
|
|
21
|
+
if (elt.querySelectorAll) {
|
|
22
|
+
var children = elt.querySelectorAll("[remove-me], [data-remove-me]");
|
|
23
|
+
for (var i = 0; i < children.length; i++) {
|
|
24
|
+
maybeRemoveMe(children[i]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
})();
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
(function(){
|
|
2
|
+
|
|
3
|
+
if (htmx.version && !htmx.version.startsWith("1.")) {
|
|
4
|
+
console.warn("WARNING: You are using an htmx 1 extension with htmx " + htmx.version +
|
|
5
|
+
". It is recommended that you move to the version of this extension found on https://extensions.htmx.org")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** @type {import("../htmx").HtmxInternalApi} */
|
|
9
|
+
var api;
|
|
10
|
+
|
|
11
|
+
var attrPrefix = 'hx-target-';
|
|
12
|
+
|
|
13
|
+
// IE11 doesn't support string.startsWith
|
|
14
|
+
function startsWith(str, prefix) {
|
|
15
|
+
return str.substring(0, prefix.length) === prefix
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {HTMLElement} elt
|
|
20
|
+
* @param {number} respCode
|
|
21
|
+
* @returns {HTMLElement | null}
|
|
22
|
+
*/
|
|
23
|
+
function getRespCodeTarget(elt, respCodeNumber) {
|
|
24
|
+
if (!elt || !respCodeNumber) return null;
|
|
25
|
+
|
|
26
|
+
var respCode = respCodeNumber.toString();
|
|
27
|
+
|
|
28
|
+
// '*' is the original syntax, as the obvious character for a wildcard.
|
|
29
|
+
// The 'x' alternative was added for maximum compatibility with HTML
|
|
30
|
+
// templating engines, due to ambiguity around which characters are
|
|
31
|
+
// supported in HTML attributes.
|
|
32
|
+
//
|
|
33
|
+
// Start with the most specific possible attribute and generalize from
|
|
34
|
+
// there.
|
|
35
|
+
var attrPossibilities = [
|
|
36
|
+
respCode,
|
|
37
|
+
|
|
38
|
+
respCode.substr(0, 2) + '*',
|
|
39
|
+
respCode.substr(0, 2) + 'x',
|
|
40
|
+
|
|
41
|
+
respCode.substr(0, 1) + '*',
|
|
42
|
+
respCode.substr(0, 1) + 'x',
|
|
43
|
+
respCode.substr(0, 1) + '**',
|
|
44
|
+
respCode.substr(0, 1) + 'xx',
|
|
45
|
+
|
|
46
|
+
'*',
|
|
47
|
+
'x',
|
|
48
|
+
'***',
|
|
49
|
+
'xxx',
|
|
50
|
+
];
|
|
51
|
+
if (startsWith(respCode, '4') || startsWith(respCode, '5')) {
|
|
52
|
+
attrPossibilities.push('error');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (var i = 0; i < attrPossibilities.length; i++) {
|
|
56
|
+
var attr = attrPrefix + attrPossibilities[i];
|
|
57
|
+
var attrValue = api.getClosestAttributeValue(elt, attr);
|
|
58
|
+
if (attrValue) {
|
|
59
|
+
if (attrValue === "this") {
|
|
60
|
+
return api.findThisElement(elt, attr);
|
|
61
|
+
} else {
|
|
62
|
+
return api.querySelectorExt(elt, attrValue);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @param {Event} evt */
|
|
71
|
+
function handleErrorFlag(evt) {
|
|
72
|
+
if (evt.detail.isError) {
|
|
73
|
+
if (htmx.config.responseTargetUnsetsError) {
|
|
74
|
+
evt.detail.isError = false;
|
|
75
|
+
}
|
|
76
|
+
} else if (htmx.config.responseTargetSetsError) {
|
|
77
|
+
evt.detail.isError = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
htmx.defineExtension('response-targets', {
|
|
82
|
+
|
|
83
|
+
/** @param {import("../htmx").HtmxInternalApi} apiRef */
|
|
84
|
+
init: function (apiRef) {
|
|
85
|
+
api = apiRef;
|
|
86
|
+
|
|
87
|
+
if (htmx.config.responseTargetUnsetsError === undefined) {
|
|
88
|
+
htmx.config.responseTargetUnsetsError = true;
|
|
89
|
+
}
|
|
90
|
+
if (htmx.config.responseTargetSetsError === undefined) {
|
|
91
|
+
htmx.config.responseTargetSetsError = false;
|
|
92
|
+
}
|
|
93
|
+
if (htmx.config.responseTargetPrefersExisting === undefined) {
|
|
94
|
+
htmx.config.responseTargetPrefersExisting = false;
|
|
95
|
+
}
|
|
96
|
+
if (htmx.config.responseTargetPrefersRetargetHeader === undefined) {
|
|
97
|
+
htmx.config.responseTargetPrefersRetargetHeader = true;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {string} name
|
|
103
|
+
* @param {Event} evt
|
|
104
|
+
*/
|
|
105
|
+
onEvent: function (name, evt) {
|
|
106
|
+
if (name === "htmx:beforeSwap" &&
|
|
107
|
+
evt.detail.xhr &&
|
|
108
|
+
evt.detail.xhr.status !== 200) {
|
|
109
|
+
if (evt.detail.target) {
|
|
110
|
+
if (htmx.config.responseTargetPrefersExisting) {
|
|
111
|
+
evt.detail.shouldSwap = true;
|
|
112
|
+
handleErrorFlag(evt);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (htmx.config.responseTargetPrefersRetargetHeader &&
|
|
116
|
+
evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) {
|
|
117
|
+
evt.detail.shouldSwap = true;
|
|
118
|
+
handleErrorFlag(evt);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!evt.detail.requestConfig) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status);
|
|
126
|
+
if (target) {
|
|
127
|
+
handleErrorFlag(evt);
|
|
128
|
+
evt.detail.shouldSwap = true;
|
|
129
|
+
evt.detail.target = target;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
})();
|