htmx.org 1.9.9 → 1.9.10
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/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/dist/ext/path-params.js +11 -0
- package/dist/ext/sse.js +120 -87
- package/dist/htmx.d.ts +36 -0
- package/dist/htmx.js +227 -177
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.gz +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.9.10] - 2023-12-21
|
|
4
|
+
|
|
5
|
+
* `hx-on*` attributes now support the form `hx-on-`, with a trailing dash, to better support template systems (such as EJS)
|
|
6
|
+
that do not like double colons in HTML attributes.
|
|
7
|
+
* Added an `htmx.config.triggerSpecsCache` configuration property that can be set to an object to cache the trigger spec parsing
|
|
8
|
+
* Added a `path-params.js` extension for populating request paths with variable values
|
|
9
|
+
* Many smaller bug fixes & improvements
|
|
10
|
+
|
|
3
11
|
## [1.9.9] - 2023-11-21
|
|
4
12
|
|
|
5
13
|
* Allow CSS selectors with whitespace in attributes like `hx-target` by using parens or curly-braces
|
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ By removing these arbitrary constraints htmx completes HTML as a
|
|
|
33
33
|
## quick start
|
|
34
34
|
|
|
35
35
|
```html
|
|
36
|
-
<script src="https://unpkg.com/htmx.org@1.9.
|
|
36
|
+
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
|
37
37
|
<!-- have a button POST a click via AJAX -->
|
|
38
38
|
<button hx-post="/clicked" hx-swap="outerHTML">
|
|
39
39
|
Click Me
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
htmx.defineExtension('path-params', {
|
|
2
|
+
onEvent: function(name, evt) {
|
|
3
|
+
if (name === "htmx:configRequest") {
|
|
4
|
+
evt.detail.path = evt.detail.path.replace(/{([^}]+)}/g, function (_, param) {
|
|
5
|
+
var val = evt.detail.parameters[param];
|
|
6
|
+
delete evt.detail.parameters[param];
|
|
7
|
+
return val === undefined ? "{" + param + "}" : encodeURIComponent(val);
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
});
|
package/dist/ext/sse.js
CHANGED
|
@@ -5,7 +5,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
5
5
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
(function(){
|
|
8
|
+
(function() {
|
|
9
9
|
|
|
10
10
|
/** @type {import("../htmx").HtmxInternalApi} */
|
|
11
11
|
var api;
|
|
@@ -39,17 +39,19 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
39
39
|
|
|
40
40
|
switch (name) {
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return;
|
|
42
|
+
case "htmx:beforeCleanupElement":
|
|
43
|
+
var internalData = api.getInternalData(evt.target)
|
|
44
|
+
// Try to remove remove an EventSource when elements are removed
|
|
45
|
+
if (internalData.sseEventSource) {
|
|
46
|
+
internalData.sseEventSource.close();
|
|
47
|
+
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
return;
|
|
50
|
+
|
|
51
|
+
// Try to create EventSources when elements are processed
|
|
52
|
+
case "htmx:afterProcessNode":
|
|
53
|
+
ensureEventSourceOnElement(evt.target);
|
|
54
|
+
registerSSE(evt.target);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
});
|
|
@@ -66,8 +68,8 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
66
68
|
* @param {string} url
|
|
67
69
|
* @returns EventSource
|
|
68
70
|
*/
|
|
69
|
-
|
|
70
|
-
return new EventSource(url, {withCredentials:true});
|
|
71
|
+
function createEventSource(url) {
|
|
72
|
+
return new EventSource(url, { withCredentials: true });
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
function splitOnWhitespace(trigger) {
|
|
@@ -90,7 +92,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
90
92
|
function getLegacySSESwaps(elt) {
|
|
91
93
|
var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
|
|
92
94
|
var returnArr = [];
|
|
93
|
-
if (legacySSEValue) {
|
|
95
|
+
if (legacySSEValue != null) {
|
|
94
96
|
var values = splitOnWhitespace(legacySSEValue);
|
|
95
97
|
for (var i = 0; i < values.length; i++) {
|
|
96
98
|
var value = values[i].split(/:(.+)/);
|
|
@@ -103,63 +105,24 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
108
|
+
* registerSSE looks for attributes that can contain sse events, right
|
|
109
|
+
* now hx-trigger and sse-swap and adds listeners based on these attributes too
|
|
110
|
+
* the closest event source
|
|
111
|
+
*
|
|
109
112
|
* @param {HTMLElement} elt
|
|
110
|
-
* @param {number} retryCount
|
|
111
|
-
* @returns {EventSource | null}
|
|
112
113
|
*/
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
function registerSSE(elt) {
|
|
115
|
+
// Find closest existing event source
|
|
116
|
+
var sourceElement = api.getClosestMatch(elt, hasEventSource);
|
|
117
|
+
if (sourceElement == null) {
|
|
118
|
+
// api.triggerErrorEvent(elt, "htmx:noSSESourceError")
|
|
119
|
+
return null; // no eventsource in parentage, orphaned element
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
var sseURL = api.getAttributeValue(elt, "sse-connect");
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (sseURL == undefined) {
|
|
126
|
-
var legacyURL = getLegacySSEURL(elt)
|
|
127
|
-
if (legacyURL) {
|
|
128
|
-
sseURL = legacyURL;
|
|
129
|
-
} else {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Connect to the EventSource
|
|
135
|
-
var source = htmx.createEventSource(sseURL);
|
|
136
|
-
internalData.sseEventSource = source;
|
|
137
|
-
|
|
138
|
-
// Create event handlers
|
|
139
|
-
source.onerror = function (err) {
|
|
140
|
-
|
|
141
|
-
// Log an error event
|
|
142
|
-
api.triggerErrorEvent(elt, "htmx:sseError", {error:err, source:source});
|
|
122
|
+
// Set internalData and source
|
|
123
|
+
var internalData = api.getInternalData(sourceElement);
|
|
124
|
+
var source = internalData.sseEventSource;
|
|
143
125
|
|
|
144
|
-
// If parent no longer exists in the document, then clean up this EventSource
|
|
145
|
-
if (maybeCloseSSESource(elt)) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Otherwise, try to reconnect the EventSource
|
|
150
|
-
if (source.readyState === EventSource.CLOSED) {
|
|
151
|
-
retryCount = retryCount || 0;
|
|
152
|
-
var timeout = Math.random() * (2 ^ retryCount) * 500;
|
|
153
|
-
window.setTimeout(function() {
|
|
154
|
-
createEventSourceOnElement(elt, Math.min(7, retryCount+1));
|
|
155
|
-
}, timeout);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
source.onopen = function (evt) {
|
|
160
|
-
api.triggerEvent(elt, "htmx:sseOpen", {source: source});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
126
|
// Add message handlers for every `sse-swap` attribute
|
|
164
127
|
queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
|
|
165
128
|
|
|
@@ -170,23 +133,27 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
170
133
|
var sseEventNames = getLegacySSESwaps(child);
|
|
171
134
|
}
|
|
172
135
|
|
|
173
|
-
for (var i = 0
|
|
136
|
+
for (var i = 0; i < sseEventNames.length; i++) {
|
|
174
137
|
var sseEventName = sseEventNames[i].trim();
|
|
175
138
|
var listener = function(event) {
|
|
176
139
|
|
|
177
|
-
// If the
|
|
178
|
-
if (maybeCloseSSESource(
|
|
179
|
-
source.removeEventListener(sseEventName, listener);
|
|
140
|
+
// If the source is missing then close SSE
|
|
141
|
+
if (maybeCloseSSESource(sourceElement)) {
|
|
180
142
|
return;
|
|
181
143
|
}
|
|
182
144
|
|
|
145
|
+
// If the body no longer contains the element, remove the listener
|
|
146
|
+
if (!api.bodyContains(child)) {
|
|
147
|
+
source.removeEventListener(sseEventName, listener);
|
|
148
|
+
}
|
|
149
|
+
|
|
183
150
|
// swap the response into the DOM and trigger a notification
|
|
184
151
|
swap(child, event.data);
|
|
185
152
|
api.triggerEvent(elt, "htmx:sseMessage", event);
|
|
186
153
|
};
|
|
187
154
|
|
|
188
155
|
// Register the new listener
|
|
189
|
-
api.getInternalData(
|
|
156
|
+
api.getInternalData(child).sseEventListener = listener;
|
|
190
157
|
source.addEventListener(sseEventName, listener);
|
|
191
158
|
}
|
|
192
159
|
});
|
|
@@ -203,24 +170,86 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
203
170
|
if (sseEventName.slice(0, 4) != "sse:") {
|
|
204
171
|
return;
|
|
205
172
|
}
|
|
173
|
+
|
|
174
|
+
// remove the sse: prefix from here on out
|
|
175
|
+
sseEventName = sseEventName.substr(4);
|
|
206
176
|
|
|
207
|
-
var listener = function(
|
|
177
|
+
var listener = function() {
|
|
178
|
+
if (maybeCloseSSESource(sourceElement)) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
208
181
|
|
|
209
|
-
|
|
210
|
-
if (maybeCloseSSESource(elt)) {
|
|
182
|
+
if (!api.bodyContains(child)) {
|
|
211
183
|
source.removeEventListener(sseEventName, listener);
|
|
212
|
-
return;
|
|
213
184
|
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* ensureEventSourceOnElement creates a new EventSource connection on the provided element.
|
|
191
|
+
* If a usable EventSource already exists, then it is returned. If not, then a new EventSource
|
|
192
|
+
* is created and stored in the element's internalData.
|
|
193
|
+
* @param {HTMLElement} elt
|
|
194
|
+
* @param {number} retryCount
|
|
195
|
+
* @returns {EventSource | null}
|
|
196
|
+
*/
|
|
197
|
+
function ensureEventSourceOnElement(elt, retryCount) {
|
|
198
|
+
|
|
199
|
+
if (elt == null) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
214
202
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
// handle extension source creation attribute
|
|
204
|
+
queryAttributeOnThisOrChildren(elt, "sse-connect").forEach(function(child) {
|
|
205
|
+
var sseURL = api.getAttributeValue(child, "sse-connect");
|
|
206
|
+
if (sseURL == null) {
|
|
207
|
+
return;
|
|
218
208
|
}
|
|
219
209
|
|
|
220
|
-
|
|
221
|
-
api.getInternalData(elt).sseEventListener = listener;
|
|
222
|
-
source.addEventListener(sseEventName.slice(4), listener);
|
|
210
|
+
ensureEventSource(child, sseURL, retryCount);
|
|
223
211
|
});
|
|
212
|
+
|
|
213
|
+
// handle legacy sse, remove for HTMX2
|
|
214
|
+
queryAttributeOnThisOrChildren(elt, "hx-sse").forEach(function(child) {
|
|
215
|
+
var sseURL = getLegacySSEURL(child);
|
|
216
|
+
if (sseURL == null) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
ensureEventSource(child, sseURL, retryCount);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function ensureEventSource(elt, url, retryCount) {
|
|
226
|
+
var source = htmx.createEventSource(url);
|
|
227
|
+
|
|
228
|
+
source.onerror = function(err) {
|
|
229
|
+
|
|
230
|
+
// Log an error event
|
|
231
|
+
api.triggerErrorEvent(elt, "htmx:sseError", { error: err, source: source });
|
|
232
|
+
|
|
233
|
+
// If parent no longer exists in the document, then clean up this EventSource
|
|
234
|
+
if (maybeCloseSSESource(elt)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Otherwise, try to reconnect the EventSource
|
|
239
|
+
if (source.readyState === EventSource.CLOSED) {
|
|
240
|
+
retryCount = retryCount || 0;
|
|
241
|
+
var timeout = Math.random() * (2 ^ retryCount) * 500;
|
|
242
|
+
window.setTimeout(function() {
|
|
243
|
+
ensureEventSourceOnElement(elt, Math.min(7, retryCount + 1));
|
|
244
|
+
}, timeout);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
source.onopen = function(evt) {
|
|
249
|
+
api.triggerEvent(elt, "htmx:sseOpen", { source: source });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
api.getInternalData(elt).sseEventSource = source;
|
|
224
253
|
}
|
|
225
254
|
|
|
226
255
|
/**
|
|
@@ -253,12 +282,12 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
253
282
|
var result = [];
|
|
254
283
|
|
|
255
284
|
// If the parent element also contains the requested attribute, then add it to the results too.
|
|
256
|
-
if (api.hasAttribute(elt, attributeName)
|
|
285
|
+
if (api.hasAttribute(elt, attributeName)) {
|
|
257
286
|
result.push(elt);
|
|
258
287
|
}
|
|
259
288
|
|
|
260
289
|
// Search all child nodes that match the requested attribute
|
|
261
|
-
elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]
|
|
290
|
+
elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]").forEach(function(node) {
|
|
262
291
|
result.push(node);
|
|
263
292
|
});
|
|
264
293
|
|
|
@@ -281,7 +310,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
281
310
|
|
|
282
311
|
api.selectAndSwap(swapSpec.swapStyle, target, elt, content, settleInfo);
|
|
283
312
|
|
|
284
|
-
settleInfo.elts.forEach(function
|
|
313
|
+
settleInfo.elts.forEach(function(elt) {
|
|
285
314
|
if (elt.classList) {
|
|
286
315
|
elt.classList.add(htmx.config.settlingClass);
|
|
287
316
|
}
|
|
@@ -306,11 +335,11 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
306
335
|
function doSettle(settleInfo) {
|
|
307
336
|
|
|
308
337
|
return function() {
|
|
309
|
-
settleInfo.tasks.forEach(function
|
|
338
|
+
settleInfo.tasks.forEach(function(task) {
|
|
310
339
|
task.call();
|
|
311
340
|
});
|
|
312
341
|
|
|
313
|
-
settleInfo.elts.forEach(function
|
|
342
|
+
settleInfo.elts.forEach(function(elt) {
|
|
314
343
|
if (elt.classList) {
|
|
315
344
|
elt.classList.remove(htmx.config.settlingClass);
|
|
316
345
|
}
|
|
@@ -319,4 +348,8 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|
|
319
348
|
}
|
|
320
349
|
}
|
|
321
350
|
|
|
322
|
-
|
|
351
|
+
function hasEventSource(node) {
|
|
352
|
+
return api.getInternalData(node).sseEventSource != null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
})();
|
package/dist/htmx.d.ts
CHANGED
|
@@ -400,6 +400,42 @@ export interface HtmxConfig {
|
|
|
400
400
|
* @default true
|
|
401
401
|
*/
|
|
402
402
|
scrollIntoViewOnBoost?: boolean;
|
|
403
|
+
/**
|
|
404
|
+
* If set, the nonce will be added to inline scripts.
|
|
405
|
+
* @default ''
|
|
406
|
+
*/
|
|
407
|
+
inlineScriptNonce?: string;
|
|
408
|
+
/**
|
|
409
|
+
* The type of binary data being received over the WebSocket connection
|
|
410
|
+
* @default 'blob'
|
|
411
|
+
*/
|
|
412
|
+
wsBinaryType?: 'blob' | 'arraybuffer';
|
|
413
|
+
/**
|
|
414
|
+
* If set to true htmx will include a cache-busting parameter in GET requests to avoid caching partial responses by the browser
|
|
415
|
+
* @default false
|
|
416
|
+
*/
|
|
417
|
+
getCacheBusterParam?: boolean;
|
|
418
|
+
/**
|
|
419
|
+
* If set to true, htmx will use the View Transition API when swapping in new content.
|
|
420
|
+
* @default false
|
|
421
|
+
*/
|
|
422
|
+
globalViewTransitions?: boolean;
|
|
423
|
+
/**
|
|
424
|
+
* htmx will format requests with these methods by encoding their parameters in the URL, not the request body
|
|
425
|
+
* @default ["get"]
|
|
426
|
+
*/
|
|
427
|
+
methodsThatUseUrlParams?: ('get' | 'head' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | 'patch' )[];
|
|
428
|
+
/**
|
|
429
|
+
* If set to true htmx will not update the title of the document when a title tag is found in new content
|
|
430
|
+
* @default false
|
|
431
|
+
*/
|
|
432
|
+
ignoreTitle:? boolean;
|
|
433
|
+
/**
|
|
434
|
+
* The cache to store evaluated trigger specifications into.
|
|
435
|
+
* You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
|
436
|
+
* @default null
|
|
437
|
+
*/
|
|
438
|
+
triggerSpecsCache?: {[trigger: string]: HtmxTriggerSpecification[]};
|
|
403
439
|
}
|
|
404
440
|
|
|
405
441
|
/**
|