mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 15:25:26 +00:00
Rebuild SSE using new Extension API
- internal API has been moved to a constructor variable. - added reconnect with exponential backoff - initial tests of SSE features are working - added many JSDoc comments in the process. - added link to "sse triggers" test
This commit is contained in:
parent
e3e83df5ad
commit
2aca696c88
220
src/ext/sse.js
220
src/ext/sse.js
@ -8,7 +8,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
(function(){
|
(function(){
|
||||||
|
|
||||||
/** @type {import("../htmx").HtmxInternalApi} */
|
/** @type {import("../htmx").HtmxInternalApi} */
|
||||||
var api
|
var api;
|
||||||
|
|
||||||
htmx.defineExtension("sse", {
|
htmx.defineExtension("sse", {
|
||||||
|
|
||||||
@ -16,11 +16,16 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
* Init saves the provided reference to the internal HTMX API.
|
* Init saves the provided reference to the internal HTMX API.
|
||||||
*
|
*
|
||||||
* @param {import("../htmx").HtmxInternalApi} api
|
* @param {import("../htmx").HtmxInternalApi} api
|
||||||
* @param {HTMLElement} elt
|
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
init: function(apiRef, _elt) {
|
init: function(apiRef) {
|
||||||
api = apiRef
|
// store a reference to the internal API.
|
||||||
|
api = apiRef;
|
||||||
|
|
||||||
|
// set a function in the public API for creating new EventSource objects
|
||||||
|
if (htmx.createEventSource == undefined) {
|
||||||
|
htmx.createEventSource = createEventSource;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,89 +41,15 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
|
|
||||||
// Try to remove remove an EventSource when elements are removed
|
// Try to remove remove an EventSource when elements are removed
|
||||||
case "htmx:beforeCleanupElement":
|
case "htmx:beforeCleanupElement":
|
||||||
var source = api.getInternalData(evt.target, "sseEventSource")
|
var internalData = api.getInternalData(evt.target)
|
||||||
if (source != null) {
|
if (internalData.sseEventSource) {
|
||||||
source.close();
|
internalData.sseEventSource.close();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Try to create EventSources when elements are processed
|
// Try to create EventSources when elements are processed
|
||||||
case "htmx:afterProcessNode":
|
case "htmx:afterProcessNode":
|
||||||
|
createEventSourceOnElement(evt.target);
|
||||||
var parent = evt.target;
|
|
||||||
|
|
||||||
// get URL from element's attribute
|
|
||||||
var sseURL = api.getAttributeValue(evt.target, "sse-url")
|
|
||||||
|
|
||||||
if (sseURL == undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default function for creating new EventSource objects
|
|
||||||
if (htmx.createEventSource == undefined) {
|
|
||||||
htmx.createEventSource = createEventSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to the EventSource
|
|
||||||
var source = htmx.createEventSource(sseURL);
|
|
||||||
|
|
||||||
source.onerror = function (err) {
|
|
||||||
api.triggerErrorEvent(parent, "htmx:sseError", {error:err, source:source});
|
|
||||||
maybeCloseSSESource(api, parent);
|
|
||||||
};
|
|
||||||
|
|
||||||
api.getInternalData(parent).sseEventSource = source;
|
|
||||||
|
|
||||||
// Add message handlers for every `sse-swap` attribute
|
|
||||||
queryAttributeOnThisOrChildren(api, parent, "sse-swap").forEach(function(child) {
|
|
||||||
|
|
||||||
var sseEventName = api.getAttributeValue(child, "sse-swap")
|
|
||||||
|
|
||||||
var listener = function(event) {
|
|
||||||
|
|
||||||
// If the parent is missing then close SSE and remove listener
|
|
||||||
if (maybeCloseSSESource(api, parent)) {
|
|
||||||
source.removeEventListener(sseEventName, listener);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// swap the response into the DOM and trigger a notification
|
|
||||||
api.swap(child, event.data)
|
|
||||||
api.triggerEvent(parent, "htmx:sseMessage", event)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Register the new listener
|
|
||||||
api.getInternalData(parent).sseEventListener = listener;
|
|
||||||
source.addEventListener(sseEventName, listener);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add message handlers for every `hx-trigger="sse:*"` attribute
|
|
||||||
queryAttributeOnThisOrChildren(api, parent, "hx-trigger").forEach(function(child) {
|
|
||||||
|
|
||||||
var sseEventName = api.getAttributeValue(child, "hx-trigger")
|
|
||||||
|
|
||||||
// Only process hx-triggers for events with the "sse:" prefix
|
|
||||||
if (sseEventName.slice(0, 4) != "sse:") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var listener = function(event) {
|
|
||||||
|
|
||||||
// If parent is missing, then close SSE and remove listener
|
|
||||||
if (maybeCloseSSESource(api, parent)) {
|
|
||||||
source.removeEventListener(sseEventName, listener);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger events to be handled by the rest of htmx
|
|
||||||
htmx.trigger(child, sseEventName, event)
|
|
||||||
htmx.trigger(child, "htmx:sseMessage", event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the new listener
|
|
||||||
api.getInternalData(parent).sseEventListener = listener;
|
|
||||||
source.addEventListener(sseEventName.slice(4), listener);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -135,8 +66,104 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @returns EventSource
|
* @returns EventSource
|
||||||
*/
|
*/
|
||||||
function createEventSource(url){
|
function createEventSource(url) {
|
||||||
return new EventSource(url, {withCredentials:true})
|
return new EventSource(url, {withCredentials:true});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createEventSourceOnElement creates a new EventSource connection on the provided element.
|
||||||
|
* If a usable EventSource already exists, then it is returned. If not, then a new EventSource
|
||||||
|
* is created and stored in the element's internalData.
|
||||||
|
* @param {HTMLElement} parent
|
||||||
|
* @param {number} retryCount
|
||||||
|
* @returns {EventSource | null}
|
||||||
|
*/
|
||||||
|
function createEventSourceOnElement(parent, retryCount) {
|
||||||
|
|
||||||
|
var internalData = api.getInternalData(parent);
|
||||||
|
|
||||||
|
// get URL from element's attribute
|
||||||
|
var sseURL = api.getAttributeValue(parent, "sse-url");
|
||||||
|
|
||||||
|
if (sseURL == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the EventSource
|
||||||
|
var source = htmx.createEventSource(sseURL);
|
||||||
|
internalData.sseEventSource = source;
|
||||||
|
|
||||||
|
// Create event handlers
|
||||||
|
source.onerror = function (err) {
|
||||||
|
|
||||||
|
// Log an error event
|
||||||
|
api.triggerErrorEvent(parent, "htmx:sseError", {error:err, source:source});
|
||||||
|
|
||||||
|
// If parent no longer exists in the document, then clean up this EventSource
|
||||||
|
if (maybeCloseSSESource(parent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, try to reconnect the EventSource
|
||||||
|
if (source.readyState == EventSource.CLOSED) {
|
||||||
|
retryCount = retryCount || 0;
|
||||||
|
var timeout = Math.random() * (2 ^ retryCount) * 500;
|
||||||
|
window.setTimeout(function() {
|
||||||
|
createEventSourceOnElement(parent, Math.min(7, retryCount+1));
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add message handlers for every `sse-swap` attribute
|
||||||
|
queryAttributeOnThisOrChildren(parent, "sse-swap").forEach(function(child) {
|
||||||
|
|
||||||
|
var sseEventName = api.getAttributeValue(child, "sse-swap");
|
||||||
|
|
||||||
|
var listener = function(event) {
|
||||||
|
|
||||||
|
// If the parent is missing then close SSE and remove listener
|
||||||
|
if (maybeCloseSSESource(parent)) {
|
||||||
|
source.removeEventListener(sseEventName, listener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// swap the response into the DOM and trigger a notification
|
||||||
|
swap(child, event.data);
|
||||||
|
api.triggerEvent(parent, "htmx:sseMessage", event);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the new listener
|
||||||
|
api.getInternalData(parent).sseEventListener = listener;
|
||||||
|
source.addEventListener(sseEventName, listener);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add message handlers for every `hx-trigger="sse:*"` attribute
|
||||||
|
queryAttributeOnThisOrChildren(parent, "hx-trigger").forEach(function(child) {
|
||||||
|
|
||||||
|
var sseEventName = api.getAttributeValue(child, "hx-trigger");
|
||||||
|
|
||||||
|
// Only process hx-triggers for events with the "sse:" prefix
|
||||||
|
if (sseEventName.slice(0, 4) != "sse:") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener = function(event) {
|
||||||
|
|
||||||
|
// If parent is missing, then close SSE and remove listener
|
||||||
|
if (maybeCloseSSESource(parent)) {
|
||||||
|
source.removeEventListener(sseEventName, listener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger events to be handled by the rest of htmx
|
||||||
|
htmx.trigger(child, sseEventName, event);
|
||||||
|
htmx.trigger(child, "htmx:sseMessage", event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the new listener
|
||||||
|
api.getInternalData(parent).sseEventListener = listener;
|
||||||
|
source.addEventListener(sseEventName.slice(4), listener);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,9 +175,9 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
*/
|
*/
|
||||||
function maybeCloseSSESource(elt) {
|
function maybeCloseSSESource(elt) {
|
||||||
if (!api.bodyContains(elt)) {
|
if (!api.bodyContains(elt)) {
|
||||||
var source = api.getInternalData("sseEventSource")
|
var source = api.getInternalData(elt, "sseEventSource");
|
||||||
if (source != undefined) {
|
if (source != undefined) {
|
||||||
source.close()
|
source.close();
|
||||||
// source = null
|
// source = null
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -166,7 +193,7 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
*/
|
*/
|
||||||
function queryAttributeOnThisOrChildren(elt, attributeName) {
|
function queryAttributeOnThisOrChildren(elt, attributeName) {
|
||||||
|
|
||||||
var result = []
|
var result = [];
|
||||||
|
|
||||||
// If the parent element also contains the requested attribute, then add it to the results too.
|
// If the parent element also contains the requested attribute, then add it to the results too.
|
||||||
if (api.hasAttribute(elt, attributeName)) {
|
if (api.hasAttribute(elt, attributeName)) {
|
||||||
@ -175,10 +202,10 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
|
|
||||||
// Search all child nodes that match the requested attribute
|
// Search all child nodes that match the requested attribute
|
||||||
elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]").forEach(function(node) {
|
elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]").forEach(function(node) {
|
||||||
result.push(node)
|
result.push(node);
|
||||||
})
|
});
|
||||||
|
|
||||||
return result
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,16 +213,17 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
|
|||||||
* @param {string} content
|
* @param {string} content
|
||||||
*/
|
*/
|
||||||
function swap(elt, content) {
|
function swap(elt, content) {
|
||||||
api.withExtensions(elt, function(extension){
|
|
||||||
|
api.withExtensions(elt, function(extension) {
|
||||||
content = extension.transformResponse(content, null, elt);
|
content = extension.transformResponse(content, null, elt);
|
||||||
});
|
});
|
||||||
|
|
||||||
var swapSpec = api.getSwapSpecification(elt)
|
var swapSpec = api.getSwapSpecification(elt);
|
||||||
var target = api.getTarget(elt)
|
var target = api.getTarget(elt);
|
||||||
var settleInfo = api.makeSettleInfo(elt);
|
var settleInfo = api.makeSettleInfo(elt);
|
||||||
|
|
||||||
selectAndSwap(swapSpec.swapStyle, elt, target, content, settleInfo)
|
api.selectAndSwap(swapSpec.swapStyle, elt, target, content, settleInfo);
|
||||||
settleImmediately(settleInfo.tasks)
|
api.settleImmediately(settleInfo.tasks);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
16
src/htmx.d.ts
vendored
16
src/htmx.d.ts
vendored
@ -1,6 +1,7 @@
|
|||||||
export interface HtmxApi {
|
export interface HtmxApi {
|
||||||
config?: HtmxConfig
|
config?: HtmxConfig
|
||||||
logger?: (a: HTMLElement, b: Event, c: any) => void | null
|
logger?: (a: HTMLElement, b: Event, c: any) => void | null
|
||||||
|
on: (event:string, listener:EventListener) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HtmxConfig {
|
export interface HtmxConfig {
|
||||||
@ -15,12 +16,15 @@ export interface HtmxConfig {
|
|||||||
requestClass?: 'htmx-request' | string;
|
requestClass?: 'htmx-request' | string;
|
||||||
settlingClass?: 'htmx-settling' | string;
|
settlingClass?: 'htmx-settling' | string;
|
||||||
swappingClass?: 'htmx-swapping' | string;
|
swappingClass?: 'htmx-swapping' | string;
|
||||||
|
addedClass?: string;
|
||||||
allowEval?: boolean;
|
allowEval?: boolean;
|
||||||
|
timeout: number;
|
||||||
attributesToSettle?: ["class", "style", "width", "height"] | string[];
|
attributesToSettle?: ["class", "style", "width", "height"] | string[];
|
||||||
withCredentials?: boolean;
|
withCredentials?: boolean;
|
||||||
wsReconnectDelay?: 'full-jitter' | string;
|
wsReconnectDelay?: 'full-jitter' | string;
|
||||||
disableSelector?: "[hx-disable], [data-hx-disable]" | string;
|
disableSelector?: "[hx-disable], [data-hx-disable]" | string;
|
||||||
useTemplateFragments?: boolean;
|
useTemplateFragments?: boolean;
|
||||||
|
scrollBehavior: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare var htmx: HtmxApi
|
export declare var htmx: HtmxApi
|
||||||
@ -41,18 +45,20 @@ export interface HtmxInternalApi {
|
|||||||
getInternalData: (element: HTMLElement) => Object
|
getInternalData: (element: HTMLElement) => Object
|
||||||
getSwapSpecification: (element: HTMLElement) => HtmxSwapSpecification
|
getSwapSpecification: (element: HTMLElement) => HtmxSwapSpecification
|
||||||
getTarget: (element: HTMLElement) => object
|
getTarget: (element: HTMLElement) => object
|
||||||
|
makeSettleInfo: () => Object
|
||||||
|
selectAndSwap: (swapStyle: any, target: any, elt: any, responseText: any, settleInfo: any) => void // TODO: improve parameter definitions
|
||||||
|
settleImmediately: (tasks: any) => void // TODO: improve parameter definitions
|
||||||
triggerEvent: (element: HTMLElement, eventName: string, detail: any) => void
|
triggerEvent: (element: HTMLElement, eventName: string, detail: any) => void
|
||||||
triggerErrorEvent: (element: HTMLElement, eventName: string, detail: any) => void
|
triggerErrorEvent: (element: HTMLElement, eventName: string, detail: any) => void
|
||||||
withExtensions: (element: HTMLElement, toDo:(ext:HtmxExtension) => void) => void
|
withExtensions: (element: HTMLElement, toDo:(ext:HtmxExtension) => void) => void
|
||||||
swap: (element: HTMLElement, content: string) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HtmxSwapSpecification {
|
export interface HtmxSwapSpecification {
|
||||||
swapStyle: string
|
swapStyle: string
|
||||||
swapDelay: number
|
swapDelay: number
|
||||||
settleDelay: number
|
settleDelay: number
|
||||||
show:? string
|
show?: string
|
||||||
showTarget:? string
|
showTarget?: string
|
||||||
scroll:? string
|
scroll?: string
|
||||||
scrollTarget:? string
|
scrollTarget?: string
|
||||||
}
|
}
|
48
src/htmx.js
48
src/htmx.js
@ -14,6 +14,8 @@ return (function () {
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
|
//** @type {import("./htmx").HtmxApi} */
|
||||||
|
// TODO: list all methods in public API
|
||||||
var htmx = {
|
var htmx = {
|
||||||
onLoad: onLoadHelper,
|
onLoad: onLoadHelper,
|
||||||
process: processNode,
|
process: processNode,
|
||||||
@ -67,6 +69,22 @@ return (function () {
|
|||||||
version: "1.6.0"
|
version: "1.6.0"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @type {import("./htmx").HtmxInternalApi} */
|
||||||
|
var internalAPI = {
|
||||||
|
bodyContains: bodyContains,
|
||||||
|
hasAttribute: hasAttribute,
|
||||||
|
getAttributeValue: getAttributeValue,
|
||||||
|
getInternalData: getInternalData,
|
||||||
|
getSwapSpecification: getSwapSpecification,
|
||||||
|
getTarget: getTarget,
|
||||||
|
makeSettleInfo: makeSettleInfo,
|
||||||
|
selectAndSwap: selectAndSwap,
|
||||||
|
settleImmediately: settleImmediately,
|
||||||
|
triggerEvent: triggerEvent,
|
||||||
|
triggerErrorEvent: triggerErrorEvent,
|
||||||
|
withExtensions: withExtensions,
|
||||||
|
}
|
||||||
|
|
||||||
var VERBS = ['get', 'post', 'put', 'delete', 'patch'];
|
var VERBS = ['get', 'post', 'put', 'delete', 'patch'];
|
||||||
var VERB_SELECTOR = VERBS.map(function(verb){
|
var VERB_SELECTOR = VERBS.map(function(verb){
|
||||||
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
return "[hx-" + verb + "], [data-hx-" + verb + "]"
|
||||||
@ -154,10 +172,9 @@ return (function () {
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function matches(elt, selector) {
|
function matches(elt, selector) {
|
||||||
|
// @ts-ignore: non-standard properties for browser compatability
|
||||||
// noinspection JSUnresolvedVariable
|
// noinspection JSUnresolvedVariable
|
||||||
var matchesFunction = elt.matches ||
|
var matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector;
|
||||||
elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector
|
|
||||||
|| elt.webkitMatchesSelector || elt.oMatchesSelector;
|
|
||||||
return matchesFunction && matchesFunction.call(elt, selector);
|
return matchesFunction && matchesFunction.call(elt, selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +262,11 @@ return (function () {
|
|||||||
return isType(o, "Object");
|
return isType(o, "Object");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getInternalData retrieves "private" data stored by htmx within an element
|
||||||
|
* @param {HTMLElement} elt
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
function getInternalData(elt) {
|
function getInternalData(elt) {
|
||||||
var dataProp = 'htmx-internal-data';
|
var dataProp = 'htmx-internal-data';
|
||||||
var data = elt[dataProp];
|
var data = elt[dataProp];
|
||||||
@ -254,6 +276,11 @@ return (function () {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toArray converts an ArrayLike object into a real array.
|
||||||
|
* @param {ArrayLike} arr
|
||||||
|
* @returns {any[]}
|
||||||
|
*/
|
||||||
function toArray(arr) {
|
function toArray(arr) {
|
||||||
var returnArr = [];
|
var returnArr = [];
|
||||||
if (arr) {
|
if (arr) {
|
||||||
@ -647,12 +674,14 @@ return (function () {
|
|||||||
if (target.tagName === "BODY") {
|
if (target.tagName === "BODY") {
|
||||||
return swapInnerHTML(target, fragment, settleInfo);
|
return swapInnerHTML(target, fragment, settleInfo);
|
||||||
} else {
|
} else {
|
||||||
|
// @type {HTMLElement}
|
||||||
|
var newElt
|
||||||
var eltBeforeNewContent = target.previousSibling;
|
var eltBeforeNewContent = target.previousSibling;
|
||||||
insertNodesBefore(parentElt(target), target, fragment, settleInfo);
|
insertNodesBefore(parentElt(target), target, fragment, settleInfo);
|
||||||
if (eltBeforeNewContent == null) {
|
if (eltBeforeNewContent == null) {
|
||||||
var newElt = parentElt(target).firstChild;
|
newElt = parentElt(target).firstChild;
|
||||||
} else {
|
} else {
|
||||||
var newElt = eltBeforeNewContent.nextSibling;
|
newElt = eltBeforeNewContent.nextSibling;
|
||||||
}
|
}
|
||||||
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
|
||||||
settleInfo.elts = [] // clear existing elements
|
settleInfo.elts = [] // clear existing elements
|
||||||
@ -1911,7 +1940,7 @@ return (function () {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} elt
|
* @param {HTMLElement} elt
|
||||||
* @returns import("./htmx").HtmxSwapSpecification
|
* @returns {import("./htmx").HtmxSwapSpecification}
|
||||||
*/
|
*/
|
||||||
function getSwapSpecification(elt) {
|
function getSwapSpecification(elt) {
|
||||||
var swapInfo = getClosestAttributeValue(elt, "hx-swap");
|
var swapInfo = getClosestAttributeValue(elt, "hx-swap");
|
||||||
@ -2560,6 +2589,11 @@ return (function () {
|
|||||||
|
|
||||||
/** @type {Object<string, import("./htmx").HtmxExtension>} */
|
/** @type {Object<string, import("./htmx").HtmxExtension>} */
|
||||||
var extensions = {};
|
var extensions = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extensionBase defines the default functions for all extensions.
|
||||||
|
* @returns {import("./htmx").HtmxExtension}
|
||||||
|
*/
|
||||||
function extensionBase() {
|
function extensionBase() {
|
||||||
return {
|
return {
|
||||||
init: function(api) {return null;},
|
init: function(api) {return null;},
|
||||||
@ -2578,7 +2612,7 @@ return (function () {
|
|||||||
* @param {import("./htmx").HtmxExtension} extension
|
* @param {import("./htmx").HtmxExtension} extension
|
||||||
*/
|
*/
|
||||||
function defineExtension(name, extension) {
|
function defineExtension(name, extension) {
|
||||||
extension.init(this)
|
extension.init(internalAPI)
|
||||||
extensions[name] = mergeObjects(extensionBase(), extension);
|
extensions[name] = mergeObjects(extensionBase(), extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="sse.html">Core SSE Test</a></li>
|
<li><a href="sse.html">Core SSE Test</a></li>
|
||||||
<li><a href="sse-multichannel.html">SSE Multichannel</a></li>
|
<li><a href="sse-multichannel.html">SSE Multichannel</a></li>
|
||||||
|
<li><a href="sse-multichannel.html">SSE Triggers</a></li>
|
||||||
<li><a href="sse-settle.html">SSE Settle</a></li>
|
<li><a href="sse-settle.html">SSE Settle</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user