Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 39 additions & 24 deletions src/editors/jetbrains/htmx.web-types.json
Original file line number Diff line number Diff line change
Expand Up @@ -450,25 +450,40 @@
"description": "Fires immediately before `fetch()` is called. `detail.ctx` is the request context. Cancel to stop the request.",
"doc-url": "https://four.htmx.org/reference/events/htmx-before-request"
},
{
"name": "after:request",
"description": "Fires immediately after `fetch()` resolves. `detail.ctx.response` is available, but the body is unconsumed.",
"doc-url": "https://four.htmx.org/reference/events/htmx-after-request"
},
{
"name": "before:response",
"description": "Fires after response headers are received but before the body is consumed. `detail.ctx.response.raw` is the unconsumed `Response`.",
"description": "Fires before the response body is consumed. `detail.ctx.response.raw` is the unconsumed `Response`.",
"doc-url": "https://four.htmx.org/reference/events/htmx-before-response"
},
{
"name": "after:request",
"description": "Fires after the response body is consumed. `detail.ctx` contains request, response, and text data.",
"doc-url": "https://four.htmx.org/reference/events/htmx-after-request"
"name": "after:response",
"description": "Fires after the response body is stored in `detail.ctx.swap.content`.",
"doc-url": "https://four.htmx.org/reference/events/htmx-after-response"
},
{
"name": "response:error",
"description": "Fires when an HTTP error status code (400 or higher) is received. `detail.ctx.response.status` contains the status code.",
"doc-url": "https://four.htmx.org/reference/events/htmx-response-error"
},
{
"name": "finally:request",
"description": "Always fires at the end of the request lifecycle. `detail.ctx` is the request context.",
"doc-url": "https://four.htmx.org/reference/events/htmx-finally-request"
"name": "before:actions",
"description": "Fires before a set of actions executes. `detail.actions` holds the actions; cancel to skip execution.",
"doc-url": "https://four.htmx.org/reference/events/htmx-before-actions"
},
{
"name": "after:actions",
"description": "Fires after a set of actions executes. `detail.actions` holds the actions.",
"doc-url": "https://four.htmx.org/reference/events/htmx-after-actions"
},
{
"name": "done",
"description": "Fires when the admitted request pipeline ends, whether it completes, fails, or is cancelled. `detail.ctx` is the request context.",
"doc-url": "https://four.htmx.org/reference/events/htmx-done"
},
{
"name": "error",
Expand Down Expand Up @@ -566,18 +581,18 @@
"doc-url": "https://four.htmx.org/extensions/hx-sse#htmxafterssemessage"
},
{
"name": "before:ws:connection",
"name": "ws:before:connection",
"description": "Fires before a WebSocket connection attempt. `detail.connection` can be modified; set `cancelled` or cancel the event to stop connecting.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxbeforewsconnection"
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsbeforeconnection"
},
{
"name": "after:ws:connection",
"name": "ws:after:connection",
"description": "Fires after a successful WebSocket connection. `detail.connection` describes the connection.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxafterwsconnection"
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsafterconnection"
},
{
"name": "ws:close",
"description": "Fires when a WebSocket connection closes. `detail.connection`, `detail.reason`, and `detail.code` describe the close.",
"description": "Fires when a WebSocket connection closes. `detail.connection`, `detail.reason`, and `detail.code` describe the close. Codes in `detail.connection.config.reconnectCodes` reconnect.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsclose"
},
{
Expand All @@ -586,24 +601,24 @@
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwserror"
},
{
"name": "before:ws:request",
"description": "Fires before sending a WebSocket message. `detail.headers` and `detail.body` are modifiable. Cancel to skip sending.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxbeforewsrequest"
"name": "ws:before:message:outgoing",
"description": "Fires before sending a WebSocket message. Modify `detail.message`, use `detail.waitUntil()` to delay sending, or set `detail.cancelled` to cancel.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsbeforemessageoutgoing"
},
{
"name": "after:ws:request",
"description": "Fires after a WebSocket message is sent. `detail.headers` and `detail.body` contain the sent payload.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxafterwsrequest"
"name": "ws:after:message:outgoing",
"description": "Fires after a WebSocket message is sent. `detail.message.data` is the value passed to `WebSocket.send()`.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsaftermessageoutgoing"
},
{
"name": "before:ws:message",
"description": "Fires before a WebSocket message is processed. `detail.message.text`, `detail.message.json`, and `detail.message.cancelled` are available.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxbeforewsmessage"
"name": "ws:before:message:incoming",
"description": "Fires before an incoming WebSocket message is processed. Convert `detail.message`, use `detail.waitUntil()` to delay processing, or set `detail.cancelled` to cancel. Associated messages fire from their sending element.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsbeforemessageincoming"
},
{
"name": "after:ws:message",
"description": "Fires after a WebSocket message is processed. `detail.message.text` and `detail.message.json` are available.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxafterwsmessage"
"name": "ws:after:message:incoming",
"description": "Fires after an incoming WebSocket message is processed. `detail.message.data` preserves the native data; `text()`, `json()`, `blob()`, and `arrayBuffer()` convert it. Associated messages fire from their sending element.",
"doc-url": "https://four.htmx.org/extensions/hx-ws#htmxwsaftermessageincoming"
},
{
"name": "download:start",
Expand Down
2 changes: 1 addition & 1 deletion src/ext/htmx-2-compat.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
maybeRetriggerEvent(elt, "htmx:afterProcessNode", detail);
maybeRetriggerEvent(elt, "htmx:load", detail);
},
htmx_after_request: function (elt, detail) {
htmx_after_response: function (elt, detail) {
maybeRetriggerEvent(elt, "htmx:afterRequest", detail);
},
htmx_after_swap: function (elt, detail) {
Expand Down
2 changes: 1 addition & 1 deletion src/ext/hx-alpine-compat.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
maybeFlush();
},

htmx_finally_request: (elt, detail) => {
htmx_done: (elt, detail) => {
if (!detail.ctx._alpineFlushed) maybeFlush();
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/ext/hx-browser-indicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
if (detail.ctx.request?.abort) activeAborts.add(detail.ctx.request.abort);
},

htmx_finally_request: (elt, detail) => {
htmx_done: (elt, detail) => {
if (!detail.ctx._browserIndicator) return;
if (detail.ctx.request?.abort) activeAborts.delete(detail.ctx.request.abort);
if (activeCount === 0) return;
Expand Down
8 changes: 4 additions & 4 deletions src/ext/hx-csp.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@

// Rewrites response nonces to pageNonce in raw HTML before fragment parsing.
// Always scrubs stolen pageNonce. Only promotes response nonce for verified same-origin.
htmx_after_request: (elt, detail) => {
htmx_after_response: (elt, detail) => {
if (!pageNonce) return false;
let ctx = detail.ctx;

// Always scrub stolen pageNonce from any response — the server cannot know the
// page nonce, so its presence indicates a stolen-nonce injection attempt.
ctx.text = rewriteNoncesInText(ctx.text, pageNonce, '');
ctx.swap.content = rewriteNoncesInText(ctx.swap.content, pageNonce, '');

// Only promote response nonce for verified same-origin responses
let responseURL = ctx?.response?.raw?.url;
Expand All @@ -186,9 +186,9 @@
catch (_) { return; }

let responseNonce = extractNonceFromCSP(ctx?.response?.headers?.get('Content-Security-Policy'))
?? extractNonceFromMetaTag(ctx?.text);
?? extractNonceFromMetaTag(ctx.swap.content);
if (responseNonce && responseNonce !== pageNonce) {
ctx.text = rewriteNoncesInText(ctx.text, responseNonce);
ctx.swap.content = rewriteNoncesInText(ctx.swap.content, responseNonce);
}
},

Expand Down
2 changes: 1 addition & 1 deletion src/ext/hx-download.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
return;
}
let cd = ctx.response.headers.get('Content-Disposition');
if (ctx.swap !== 'download' && !cd?.includes('attachment')) return;
if (ctx.swap.style !== 'download' && !cd?.includes('attachment')) return;
streamDownload(ctx.sourceElement, ctx.response.raw, ctx.request.action);
return false;
}
Expand Down
22 changes: 13 additions & 9 deletions src/ext/hx-head.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
(function () {

let api
let deferredHeadScripts = new WeakMap()

// Appends a new head node, returning a promise for render-critical resources
// (blocking scripts, stylesheets) or null for fire-and-forget resources.
Expand Down Expand Up @@ -157,31 +158,34 @@
},
htmx_before_response: (elt, detail) => {
let ctx = detail.ctx
let target = ctx.target
let target = ctx.swap.target
// TODO - is there a better way to handle this? it used to be based on if the element was boosted
let defaultMergeStrategy = target === document.body ? "merge" : "append";
if (htmx.trigger(document.body, "htmx:before:head:merge", detail)) {
let realText = ctx.response.raw.text.bind(ctx.response.raw)
ctx.response.raw.text = async () => {
let text = await realText()
ctx._deferredHeadScripts = await mergeHead(text, defaultMergeStrategy)
deferredHeadScripts.set(ctx, await mergeHead(text, defaultMergeStrategy))
return text
}
}
},
htmx_after_swap: (elt, detail) => {
for (const node of detail.ctx._deferredHeadScripts || []) appendNode(node)
let deferred = deferredHeadScripts.get(detail.ctx) || []
deferredHeadScripts.delete(detail.ctx)
for (const node of deferred) appendNode(node)
},
htmx_history_cache_before_restore: (elt, detail) => {
if (detail.head) {
// mergeHead awaits stylesheets/blocking scripts, returns deferred scripts.
// Set detail.ready so history-cache awaits before swapping body.
// Stash deferred scripts on detail — history-cache copies them onto the swap ctx
// so htmx_after_swap picks them up.
detail.ready = mergeHead(detail.head, 'merge').then(deferred => {
detail._deferredHeadScripts = deferred;
});
deferredHeadScripts.set(detail, deferred)
})
}
},
htmx_history_cache_after_restore: (elt, detail) => {
let deferred = deferredHeadScripts.get(detail) || []
deferredHeadScripts.delete(detail)
for (const node of deferred) appendNode(node)
}
})

Expand Down
18 changes: 8 additions & 10 deletions src/ext/hx-history-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,21 +169,19 @@
let cachedHTML = item.content;
let restoreSwapTarget = getHistoryTarget();
let restoreSwapStyle = cfg().swapStyle;
let ctx = {
sourceElement: document.body,
target: restoreSwapTarget,
swap: restoreSwapStyle,
text: cachedHTML,
transition: false,
_deferredHeadScripts: detail._deferredHeadScripts
};
await htmx.swap(ctx);

await htmx.swap(cachedHTML, restoreSwapTarget, {
source: document.body,
style: restoreSwapStyle,
transition: false
});

document.title = item.title || document.title;
requestAnimationFrame(() => {
window.scrollTo(0, item.scroll || 0);
restoreAnnotations(getHistoryTarget());
api.triggerHtmxEvent(document, 'htmx:history:cache:after:restore', { item });
detail.item = item;
api.triggerHtmxEvent(document, 'htmx:history:cache:after:restore', detail);
});
}

Expand Down
Loading
Loading