htmx.org 1.9.10 → 2.0.0-alpha1

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.
@@ -1,130 +0,0 @@
1
- (function(){
2
-
3
- /** @type {import("../htmx").HtmxInternalApi} */
4
- var api;
5
-
6
- var attrPrefix = 'hx-target-';
7
-
8
- // IE11 doesn't support string.startsWith
9
- function startsWith(str, prefix) {
10
- return str.substring(0, prefix.length) === prefix
11
- }
12
-
13
- /**
14
- * @param {HTMLElement} elt
15
- * @param {number} respCode
16
- * @returns {HTMLElement | null}
17
- */
18
- function getRespCodeTarget(elt, respCodeNumber) {
19
- if (!elt || !respCodeNumber) return null;
20
-
21
- var respCode = respCodeNumber.toString();
22
-
23
- // '*' is the original syntax, as the obvious character for a wildcard.
24
- // The 'x' alternative was added for maximum compatibility with HTML
25
- // templating engines, due to ambiguity around which characters are
26
- // supported in HTML attributes.
27
- //
28
- // Start with the most specific possible attribute and generalize from
29
- // there.
30
- var attrPossibilities = [
31
- respCode,
32
-
33
- respCode.substr(0, 2) + '*',
34
- respCode.substr(0, 2) + 'x',
35
-
36
- respCode.substr(0, 1) + '*',
37
- respCode.substr(0, 1) + 'x',
38
- respCode.substr(0, 1) + '**',
39
- respCode.substr(0, 1) + 'xx',
40
-
41
- '*',
42
- 'x',
43
- '***',
44
- 'xxx',
45
- ];
46
- if (startsWith(respCode, '4') || startsWith(respCode, '5')) {
47
- attrPossibilities.push('error');
48
- }
49
-
50
- for (var i = 0; i < attrPossibilities.length; i++) {
51
- var attr = attrPrefix + attrPossibilities[i];
52
- var attrValue = api.getClosestAttributeValue(elt, attr);
53
- if (attrValue) {
54
- if (attrValue === "this") {
55
- return api.findThisElement(elt, attr);
56
- } else {
57
- return api.querySelectorExt(elt, attrValue);
58
- }
59
- }
60
- }
61
-
62
- return null;
63
- }
64
-
65
- /** @param {Event} evt */
66
- function handleErrorFlag(evt) {
67
- if (evt.detail.isError) {
68
- if (htmx.config.responseTargetUnsetsError) {
69
- evt.detail.isError = false;
70
- }
71
- } else if (htmx.config.responseTargetSetsError) {
72
- evt.detail.isError = true;
73
- }
74
- }
75
-
76
- htmx.defineExtension('response-targets', {
77
-
78
- /** @param {import("../htmx").HtmxInternalApi} apiRef */
79
- init: function (apiRef) {
80
- api = apiRef;
81
-
82
- if (htmx.config.responseTargetUnsetsError === undefined) {
83
- htmx.config.responseTargetUnsetsError = true;
84
- }
85
- if (htmx.config.responseTargetSetsError === undefined) {
86
- htmx.config.responseTargetSetsError = false;
87
- }
88
- if (htmx.config.responseTargetPrefersExisting === undefined) {
89
- htmx.config.responseTargetPrefersExisting = false;
90
- }
91
- if (htmx.config.responseTargetPrefersRetargetHeader === undefined) {
92
- htmx.config.responseTargetPrefersRetargetHeader = true;
93
- }
94
- },
95
-
96
- /**
97
- * @param {string} name
98
- * @param {Event} evt
99
- */
100
- onEvent: function (name, evt) {
101
- if (name === "htmx:beforeSwap" &&
102
- evt.detail.xhr &&
103
- evt.detail.xhr.status !== 200) {
104
- if (evt.detail.target) {
105
- if (htmx.config.responseTargetPrefersExisting) {
106
- evt.detail.shouldSwap = true;
107
- handleErrorFlag(evt);
108
- return true;
109
- }
110
- if (htmx.config.responseTargetPrefersRetargetHeader &&
111
- evt.detail.xhr.getAllResponseHeaders().match(/HX-Retarget:/i)) {
112
- evt.detail.shouldSwap = true;
113
- handleErrorFlag(evt);
114
- return true;
115
- }
116
- }
117
- if (!evt.detail.requestConfig) {
118
- return true;
119
- }
120
- var target = getRespCodeTarget(evt.detail.requestConfig.elt, evt.detail.xhr.status);
121
- if (target) {
122
- handleErrorFlag(evt);
123
- evt.detail.shouldSwap = true;
124
- evt.detail.target = target;
125
- }
126
- return true;
127
- }
128
- }
129
- });
130
- })();
@@ -1,15 +0,0 @@
1
- htmx.defineExtension('restored', {
2
- onEvent : function(name, evt) {
3
- if (name === 'htmx:restored'){
4
- var restoredElts = evt.detail.document.querySelectorAll(
5
- "[hx-trigger='restored'],[data-hx-trigger='restored']"
6
- );
7
- // need a better way to do this, would prefer to just trigger from evt.detail.elt
8
- var foundElt = Array.from(restoredElts).find(
9
- (x) => (x.outerHTML === evt.detail.elt.outerHTML)
10
- );
11
- var restoredEvent = evt.detail.triggerEvent(foundElt, 'restored');
12
- }
13
- return;
14
- }
15
- })
package/dist/ext/sse.js DELETED
@@ -1,355 +0,0 @@
1
- /*
2
- Server Sent Events Extension
3
- ============================
4
- This extension adds support for Server Sent Events to htmx. See /www/extensions/sse.md for usage instructions.
5
-
6
- */
7
-
8
- (function() {
9
-
10
- /** @type {import("../htmx").HtmxInternalApi} */
11
- var api;
12
-
13
- htmx.defineExtension("sse", {
14
-
15
- /**
16
- * Init saves the provided reference to the internal HTMX API.
17
- *
18
- * @param {import("../htmx").HtmxInternalApi} api
19
- * @returns void
20
- */
21
- init: function(apiRef) {
22
- // store a reference to the internal API.
23
- api = apiRef;
24
-
25
- // set a function in the public API for creating new EventSource objects
26
- if (htmx.createEventSource == undefined) {
27
- htmx.createEventSource = createEventSource;
28
- }
29
- },
30
-
31
- /**
32
- * onEvent handles all events passed to this extension.
33
- *
34
- * @param {string} name
35
- * @param {Event} evt
36
- * @returns void
37
- */
38
- onEvent: function(name, evt) {
39
-
40
- switch (name) {
41
-
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
- }
48
-
49
- return;
50
-
51
- // Try to create EventSources when elements are processed
52
- case "htmx:afterProcessNode":
53
- ensureEventSourceOnElement(evt.target);
54
- registerSSE(evt.target);
55
- }
56
- }
57
- });
58
-
59
- ///////////////////////////////////////////////
60
- // HELPER FUNCTIONS
61
- ///////////////////////////////////////////////
62
-
63
-
64
- /**
65
- * createEventSource is the default method for creating new EventSource objects.
66
- * it is hoisted into htmx.config.createEventSource to be overridden by the user, if needed.
67
- *
68
- * @param {string} url
69
- * @returns EventSource
70
- */
71
- function createEventSource(url) {
72
- return new EventSource(url, { withCredentials: true });
73
- }
74
-
75
- function splitOnWhitespace(trigger) {
76
- return trigger.trim().split(/\s+/);
77
- }
78
-
79
- function getLegacySSEURL(elt) {
80
- var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
81
- if (legacySSEValue) {
82
- var values = splitOnWhitespace(legacySSEValue);
83
- for (var i = 0; i < values.length; i++) {
84
- var value = values[i].split(/:(.+)/);
85
- if (value[0] === "connect") {
86
- return value[1];
87
- }
88
- }
89
- }
90
- }
91
-
92
- function getLegacySSESwaps(elt) {
93
- var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
94
- var returnArr = [];
95
- if (legacySSEValue != null) {
96
- var values = splitOnWhitespace(legacySSEValue);
97
- for (var i = 0; i < values.length; i++) {
98
- var value = values[i].split(/:(.+)/);
99
- if (value[0] === "swap") {
100
- returnArr.push(value[1]);
101
- }
102
- }
103
- }
104
- return returnArr;
105
- }
106
-
107
- /**
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
- *
112
- * @param {HTMLElement} elt
113
- */
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
120
- }
121
-
122
- // Set internalData and source
123
- var internalData = api.getInternalData(sourceElement);
124
- var source = internalData.sseEventSource;
125
-
126
- // Add message handlers for every `sse-swap` attribute
127
- queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
128
-
129
- var sseSwapAttr = api.getAttributeValue(child, "sse-swap");
130
- if (sseSwapAttr) {
131
- var sseEventNames = sseSwapAttr.split(",");
132
- } else {
133
- var sseEventNames = getLegacySSESwaps(child);
134
- }
135
-
136
- for (var i = 0; i < sseEventNames.length; i++) {
137
- var sseEventName = sseEventNames[i].trim();
138
- var listener = function(event) {
139
-
140
- // If the source is missing then close SSE
141
- if (maybeCloseSSESource(sourceElement)) {
142
- return;
143
- }
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
-
150
- // swap the response into the DOM and trigger a notification
151
- swap(child, event.data);
152
- api.triggerEvent(elt, "htmx:sseMessage", event);
153
- };
154
-
155
- // Register the new listener
156
- api.getInternalData(child).sseEventListener = listener;
157
- source.addEventListener(sseEventName, listener);
158
- }
159
- });
160
-
161
- // Add message handlers for every `hx-trigger="sse:*"` attribute
162
- queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) {
163
-
164
- var sseEventName = api.getAttributeValue(child, "hx-trigger");
165
- if (sseEventName == null) {
166
- return;
167
- }
168
-
169
- // Only process hx-triggers for events with the "sse:" prefix
170
- if (sseEventName.slice(0, 4) != "sse:") {
171
- return;
172
- }
173
-
174
- // remove the sse: prefix from here on out
175
- sseEventName = sseEventName.substr(4);
176
-
177
- var listener = function() {
178
- if (maybeCloseSSESource(sourceElement)) {
179
- return
180
- }
181
-
182
- if (!api.bodyContains(child)) {
183
- source.removeEventListener(sseEventName, listener);
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
- }
202
-
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;
208
- }
209
-
210
- ensureEventSource(child, sseURL, retryCount);
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;
253
- }
254
-
255
- /**
256
- * maybeCloseSSESource confirms that the parent element still exists.
257
- * If not, then any associated SSE source is closed and the function returns true.
258
- *
259
- * @param {HTMLElement} elt
260
- * @returns boolean
261
- */
262
- function maybeCloseSSESource(elt) {
263
- if (!api.bodyContains(elt)) {
264
- var source = api.getInternalData(elt).sseEventSource;
265
- if (source != undefined) {
266
- source.close();
267
- // source = null
268
- return true;
269
- }
270
- }
271
- return false;
272
- }
273
-
274
- /**
275
- * queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT.
276
- *
277
- * @param {HTMLElement} elt
278
- * @param {string} attributeName
279
- */
280
- function queryAttributeOnThisOrChildren(elt, attributeName) {
281
-
282
- var result = [];
283
-
284
- // If the parent element also contains the requested attribute, then add it to the results too.
285
- if (api.hasAttribute(elt, attributeName)) {
286
- result.push(elt);
287
- }
288
-
289
- // Search all child nodes that match the requested attribute
290
- elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]").forEach(function(node) {
291
- result.push(node);
292
- });
293
-
294
- return result;
295
- }
296
-
297
- /**
298
- * @param {HTMLElement} elt
299
- * @param {string} content
300
- */
301
- function swap(elt, content) {
302
-
303
- api.withExtensions(elt, function(extension) {
304
- content = extension.transformResponse(content, null, elt);
305
- });
306
-
307
- var swapSpec = api.getSwapSpecification(elt);
308
- var target = api.getTarget(elt);
309
- var settleInfo = api.makeSettleInfo(elt);
310
-
311
- api.selectAndSwap(swapSpec.swapStyle, target, elt, content, settleInfo);
312
-
313
- settleInfo.elts.forEach(function(elt) {
314
- if (elt.classList) {
315
- elt.classList.add(htmx.config.settlingClass);
316
- }
317
- api.triggerEvent(elt, 'htmx:beforeSettle');
318
- });
319
-
320
- // Handle settle tasks (with delay if requested)
321
- if (swapSpec.settleDelay > 0) {
322
- setTimeout(doSettle(settleInfo), swapSpec.settleDelay);
323
- } else {
324
- doSettle(settleInfo)();
325
- }
326
- }
327
-
328
- /**
329
- * doSettle mirrors much of the functionality in htmx that
330
- * settles elements after their content has been swapped.
331
- * TODO: this should be published by htmx, and not duplicated here
332
- * @param {import("../htmx").HtmxSettleInfo} settleInfo
333
- * @returns () => void
334
- */
335
- function doSettle(settleInfo) {
336
-
337
- return function() {
338
- settleInfo.tasks.forEach(function(task) {
339
- task.call();
340
- });
341
-
342
- settleInfo.elts.forEach(function(elt) {
343
- if (elt.classList) {
344
- elt.classList.remove(htmx.config.settlingClass);
345
- }
346
- api.triggerEvent(elt, 'htmx:afterSettle');
347
- });
348
- }
349
- }
350
-
351
- function hasEventSource(node) {
352
- return api.getInternalData(node).sseEventSource != null;
353
- }
354
-
355
- })();