htmx.org 1.9.9 → 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,322 +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
- // Try to remove remove an EventSource when elements are removed
43
- case "htmx:beforeCleanupElement":
44
- var internalData = api.getInternalData(evt.target)
45
- if (internalData.sseEventSource) {
46
- internalData.sseEventSource.close();
47
- }
48
- return;
49
-
50
- // Try to create EventSources when elements are processed
51
- case "htmx:afterProcessNode":
52
- createEventSourceOnElement(evt.target);
53
- }
54
- }
55
- });
56
-
57
- ///////////////////////////////////////////////
58
- // HELPER FUNCTIONS
59
- ///////////////////////////////////////////////
60
-
61
-
62
- /**
63
- * createEventSource is the default method for creating new EventSource objects.
64
- * it is hoisted into htmx.config.createEventSource to be overridden by the user, if needed.
65
- *
66
- * @param {string} url
67
- * @returns EventSource
68
- */
69
- function createEventSource(url) {
70
- return new EventSource(url, {withCredentials:true});
71
- }
72
-
73
- function splitOnWhitespace(trigger) {
74
- return trigger.trim().split(/\s+/);
75
- }
76
-
77
- function getLegacySSEURL(elt) {
78
- var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
79
- if (legacySSEValue) {
80
- var values = splitOnWhitespace(legacySSEValue);
81
- for (var i = 0; i < values.length; i++) {
82
- var value = values[i].split(/:(.+)/);
83
- if (value[0] === "connect") {
84
- return value[1];
85
- }
86
- }
87
- }
88
- }
89
-
90
- function getLegacySSESwaps(elt) {
91
- var legacySSEValue = api.getAttributeValue(elt, "hx-sse");
92
- var returnArr = [];
93
- if (legacySSEValue) {
94
- var values = splitOnWhitespace(legacySSEValue);
95
- for (var i = 0; i < values.length; i++) {
96
- var value = values[i].split(/:(.+)/);
97
- if (value[0] === "swap") {
98
- returnArr.push(value[1]);
99
- }
100
- }
101
- }
102
- return returnArr;
103
- }
104
-
105
- /**
106
- * createEventSourceOnElement creates a new EventSource connection on the provided element.
107
- * If a usable EventSource already exists, then it is returned. If not, then a new EventSource
108
- * is created and stored in the element's internalData.
109
- * @param {HTMLElement} elt
110
- * @param {number} retryCount
111
- * @returns {EventSource | null}
112
- */
113
- function createEventSourceOnElement(elt, retryCount) {
114
-
115
- if (elt == null) {
116
- return null;
117
- }
118
-
119
- var internalData = api.getInternalData(elt);
120
-
121
- // get URL from element's attribute
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});
143
-
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
- // Add message handlers for every `sse-swap` attribute
164
- queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) {
165
-
166
- var sseSwapAttr = api.getAttributeValue(child, "sse-swap");
167
- if (sseSwapAttr) {
168
- var sseEventNames = sseSwapAttr.split(",");
169
- } else {
170
- var sseEventNames = getLegacySSESwaps(child);
171
- }
172
-
173
- for (var i = 0 ; i < sseEventNames.length ; i++) {
174
- var sseEventName = sseEventNames[i].trim();
175
- var listener = function(event) {
176
-
177
- // If the parent is missing then close SSE and remove listener
178
- if (maybeCloseSSESource(elt)) {
179
- source.removeEventListener(sseEventName, listener);
180
- return;
181
- }
182
-
183
- // swap the response into the DOM and trigger a notification
184
- swap(child, event.data);
185
- api.triggerEvent(elt, "htmx:sseMessage", event);
186
- };
187
-
188
- // Register the new listener
189
- api.getInternalData(elt).sseEventListener = listener;
190
- source.addEventListener(sseEventName, listener);
191
- }
192
- });
193
-
194
- // Add message handlers for every `hx-trigger="sse:*"` attribute
195
- queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) {
196
-
197
- var sseEventName = api.getAttributeValue(child, "hx-trigger");
198
- if (sseEventName == null) {
199
- return;
200
- }
201
-
202
- // Only process hx-triggers for events with the "sse:" prefix
203
- if (sseEventName.slice(0, 4) != "sse:") {
204
- return;
205
- }
206
-
207
- var listener = function(event) {
208
-
209
- // If parent is missing, then close SSE and remove listener
210
- if (maybeCloseSSESource(elt)) {
211
- source.removeEventListener(sseEventName, listener);
212
- return;
213
- }
214
-
215
- // Trigger events to be handled by the rest of htmx
216
- htmx.trigger(child, sseEventName, event);
217
- htmx.trigger(child, "htmx:sseMessage", event);
218
- }
219
-
220
- // Register the new listener
221
- api.getInternalData(elt).sseEventListener = listener;
222
- source.addEventListener(sseEventName.slice(4), listener);
223
- });
224
- }
225
-
226
- /**
227
- * maybeCloseSSESource confirms that the parent element still exists.
228
- * If not, then any associated SSE source is closed and the function returns true.
229
- *
230
- * @param {HTMLElement} elt
231
- * @returns boolean
232
- */
233
- function maybeCloseSSESource(elt) {
234
- if (!api.bodyContains(elt)) {
235
- var source = api.getInternalData(elt).sseEventSource;
236
- if (source != undefined) {
237
- source.close();
238
- // source = null
239
- return true;
240
- }
241
- }
242
- return false;
243
- }
244
-
245
- /**
246
- * queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT.
247
- *
248
- * @param {HTMLElement} elt
249
- * @param {string} attributeName
250
- */
251
- function queryAttributeOnThisOrChildren(elt, attributeName) {
252
-
253
- var result = [];
254
-
255
- // If the parent element also contains the requested attribute, then add it to the results too.
256
- if (api.hasAttribute(elt, attributeName) || api.hasAttribute(elt, "hx-sse")) {
257
- result.push(elt);
258
- }
259
-
260
- // Search all child nodes that match the requested attribute
261
- elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "], [hx-sse], [data-hx-sse]").forEach(function(node) {
262
- result.push(node);
263
- });
264
-
265
- return result;
266
- }
267
-
268
- /**
269
- * @param {HTMLElement} elt
270
- * @param {string} content
271
- */
272
- function swap(elt, content) {
273
-
274
- api.withExtensions(elt, function(extension) {
275
- content = extension.transformResponse(content, null, elt);
276
- });
277
-
278
- var swapSpec = api.getSwapSpecification(elt);
279
- var target = api.getTarget(elt);
280
- var settleInfo = api.makeSettleInfo(elt);
281
-
282
- api.selectAndSwap(swapSpec.swapStyle, target, elt, content, settleInfo);
283
-
284
- settleInfo.elts.forEach(function (elt) {
285
- if (elt.classList) {
286
- elt.classList.add(htmx.config.settlingClass);
287
- }
288
- api.triggerEvent(elt, 'htmx:beforeSettle');
289
- });
290
-
291
- // Handle settle tasks (with delay if requested)
292
- if (swapSpec.settleDelay > 0) {
293
- setTimeout(doSettle(settleInfo), swapSpec.settleDelay);
294
- } else {
295
- doSettle(settleInfo)();
296
- }
297
- }
298
-
299
- /**
300
- * doSettle mirrors much of the functionality in htmx that
301
- * settles elements after their content has been swapped.
302
- * TODO: this should be published by htmx, and not duplicated here
303
- * @param {import("../htmx").HtmxSettleInfo} settleInfo
304
- * @returns () => void
305
- */
306
- function doSettle(settleInfo) {
307
-
308
- return function() {
309
- settleInfo.tasks.forEach(function (task) {
310
- task.call();
311
- });
312
-
313
- settleInfo.elts.forEach(function (elt) {
314
- if (elt.classList) {
315
- elt.classList.remove(htmx.config.settlingClass);
316
- }
317
- api.triggerEvent(elt, 'htmx:afterSettle');
318
- });
319
- }
320
- }
321
-
322
- })();