import type { HtmxExtension } from "htmx.org"; interface CustomHtmxExtension { name: string; extension: Partial; } export const getErrorCallbacksExt = () => { const attrPrefix = "hx-callback-"; let htmxApi: { [x: string]: any; getClosestAttributeValue: any; }; const getCallback = (elt: HTMLElement, responseCode: number) => { if (!elt || !responseCode) { return () => {}; } const code = responseCode.toString(); // '*' is the original syntax, as the obvious character for a wildcard. // The 'x' alternative was added for maximum compatibility with HTML // templating engines, due to ambiguity around which characters are // supported in HTML attributes. // // Start with the most specific possible attribute and generalize from // there. const suffixes = [ code, code.substring(0, 2) + "*", code.substring(0, 2) + "x", code.substring(0, 1) + "*", code.substring(0, 1) + "x", code.substring(0, 1) + "**", code.substring(0, 1) + "xx", "*", "x", "***", "xxx", ]; if (code.startsWith("4") || code.startsWith("5")) { suffixes.push("error"); } for (const suffix of suffixes) { const attr = attrPrefix + suffix; const callback = htmxApi?.getClosestAttributeValue(elt, attr); if (callback) { return Function("src", "target", callback); } } return () => {}; }; return { name: "error-callbacks", extension: { init: (api: any) => { htmxApi = api; }, onEvent: (name: string, event: CustomEvent) => { if (name !== "htmx:responseError") { return true; } getCallback(event.detail.requestConfig.elt, event.detail.xhr.status)( event.detail.requestConfig.elt, htmxApi.getTarget(event.detail.requestConfig.elt), ); return true; }, }, } as CustomHtmxExtension; };