Fix: stupid llm
This commit is contained in:
		
							
								
								
									
										355
									
								
								assets/htmx.sse.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								assets/htmx.sse.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,355 @@ | ||||
| /* | ||||
| Server Sent Events Extension | ||||
| ============================ | ||||
| This extension adds support for Server Sent Events to htmx.  See /www/extensions/sse.md for usage instructions. | ||||
|  | ||||
| */ | ||||
|  | ||||
| (function() { | ||||
|  | ||||
| 	/** @type {import("../htmx").HtmxInternalApi} */ | ||||
| 	var api; | ||||
|  | ||||
| 	htmx.defineExtension("sse", { | ||||
|  | ||||
| 		/** | ||||
| 		 * Init saves the provided reference to the internal HTMX API. | ||||
| 		 *  | ||||
| 		 * @param {import("../htmx").HtmxInternalApi} api  | ||||
| 		 * @returns void | ||||
| 		 */ | ||||
| 		init: function(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; | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		/** | ||||
| 		 * onEvent handles all events passed to this extension. | ||||
| 		 *  | ||||
| 		 * @param {string} name  | ||||
| 		 * @param {Event} evt  | ||||
| 		 * @returns void | ||||
| 		 */ | ||||
| 		onEvent: function(name, evt) { | ||||
|  | ||||
| 			switch (name) { | ||||
|  | ||||
| 				case "htmx:beforeCleanupElement": | ||||
| 					var internalData = api.getInternalData(evt.target) | ||||
| 					// Try to remove remove an EventSource when elements are removed | ||||
| 					if (internalData.sseEventSource) { | ||||
| 						internalData.sseEventSource.close(); | ||||
| 					} | ||||
|  | ||||
| 					return; | ||||
|  | ||||
| 				// Try to create EventSources when elements are processed | ||||
| 				case "htmx:afterProcessNode": | ||||
| 					ensureEventSourceOnElement(evt.target); | ||||
| 					registerSSE(evt.target); | ||||
| 			} | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	/////////////////////////////////////////////// | ||||
| 	// HELPER FUNCTIONS | ||||
| 	/////////////////////////////////////////////// | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * createEventSource is the default method for creating new EventSource objects. | ||||
| 	 * it is hoisted into htmx.config.createEventSource to be overridden by the user, if needed. | ||||
| 	 *  | ||||
| 	 * @param {string} url  | ||||
| 	 * @returns EventSource | ||||
| 	 */ | ||||
| 	function createEventSource(url) { | ||||
| 		return new EventSource(url, { withCredentials: true }); | ||||
| 	} | ||||
|  | ||||
| 	function splitOnWhitespace(trigger) { | ||||
| 		return trigger.trim().split(/\s+/); | ||||
| 	} | ||||
|  | ||||
| 	function getLegacySSEURL(elt) { | ||||
| 		var legacySSEValue = api.getAttributeValue(elt, "hx-sse"); | ||||
| 		if (legacySSEValue) { | ||||
| 			var values = splitOnWhitespace(legacySSEValue); | ||||
| 			for (var i = 0; i < values.length; i++) { | ||||
| 				var value = values[i].split(/:(.+)/); | ||||
| 				if (value[0] === "connect") { | ||||
| 					return value[1]; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function getLegacySSESwaps(elt) { | ||||
| 		var legacySSEValue = api.getAttributeValue(elt, "hx-sse"); | ||||
| 		var returnArr = []; | ||||
| 		if (legacySSEValue != null) { | ||||
| 			var values = splitOnWhitespace(legacySSEValue); | ||||
| 			for (var i = 0; i < values.length; i++) { | ||||
| 				var value = values[i].split(/:(.+)/); | ||||
| 				if (value[0] === "swap") { | ||||
| 					returnArr.push(value[1]); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return returnArr; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * registerSSE looks for attributes that can contain sse events, right  | ||||
| 	 * now hx-trigger and sse-swap and adds listeners based on these attributes too | ||||
| 	 * the closest event source | ||||
| 	 * | ||||
| 	 * @param {HTMLElement} elt | ||||
| 	 */ | ||||
| 	function registerSSE(elt) { | ||||
| 		// Find closest existing event source | ||||
| 		var sourceElement = api.getClosestMatch(elt, hasEventSource); | ||||
| 		if (sourceElement == null) { | ||||
| 			// api.triggerErrorEvent(elt, "htmx:noSSESourceError") | ||||
| 			return null; // no eventsource in parentage, orphaned element | ||||
| 		} | ||||
|  | ||||
| 		// Set internalData and source | ||||
| 		var internalData = api.getInternalData(sourceElement); | ||||
| 		var source = internalData.sseEventSource; | ||||
|  | ||||
| 		// Add message handlers for every `sse-swap` attribute | ||||
| 		queryAttributeOnThisOrChildren(elt, "sse-swap").forEach(function(child) { | ||||
|  | ||||
| 			var sseSwapAttr = api.getAttributeValue(child, "sse-swap"); | ||||
| 			if (sseSwapAttr) { | ||||
| 				var sseEventNames = sseSwapAttr.split(","); | ||||
| 			} else { | ||||
| 				var sseEventNames = getLegacySSESwaps(child); | ||||
| 			} | ||||
|  | ||||
| 			for (var i = 0; i < sseEventNames.length; i++) { | ||||
| 				var sseEventName = sseEventNames[i].trim(); | ||||
| 				var listener = function(event) { | ||||
|  | ||||
| 					// If the source is missing then close SSE | ||||
| 					if (maybeCloseSSESource(sourceElement)) { | ||||
| 						return; | ||||
| 					} | ||||
|  | ||||
| 					// If the body no longer contains the element, remove the listener | ||||
| 					if (!api.bodyContains(child)) { | ||||
| 						source.removeEventListener(sseEventName, listener); | ||||
| 					} | ||||
|  | ||||
| 					// swap the response into the DOM and trigger a notification | ||||
| 					swap(child, event.data); | ||||
| 					api.triggerEvent(elt, "htmx:sseMessage", event); | ||||
| 				}; | ||||
|  | ||||
| 				// Register the new listener | ||||
| 				api.getInternalData(child).sseEventListener = listener; | ||||
| 				source.addEventListener(sseEventName, listener); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		// Add message handlers for every `hx-trigger="sse:*"` attribute | ||||
| 		queryAttributeOnThisOrChildren(elt, "hx-trigger").forEach(function(child) { | ||||
|  | ||||
| 			var sseEventName = api.getAttributeValue(child, "hx-trigger"); | ||||
| 			if (sseEventName == null) { | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// Only process hx-triggers for events with the "sse:" prefix | ||||
| 			if (sseEventName.slice(0, 4) != "sse:") { | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			// remove the sse: prefix from here on out | ||||
| 			sseEventName = sseEventName.substr(4); | ||||
|  | ||||
| 			var listener = function() { | ||||
| 				if (maybeCloseSSESource(sourceElement)) { | ||||
| 					return | ||||
| 				} | ||||
|  | ||||
| 				if (!api.bodyContains(child)) { | ||||
| 					source.removeEventListener(sseEventName, listener); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * ensureEventSourceOnElement 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} elt | ||||
| 	 * @param {number} retryCount | ||||
| 	 * @returns {EventSource | null} | ||||
| 	 */ | ||||
| 	function ensureEventSourceOnElement(elt, retryCount) { | ||||
|  | ||||
| 		if (elt == null) { | ||||
| 			return null; | ||||
| 		} | ||||
|  | ||||
| 		// handle extension source creation attribute | ||||
| 		queryAttributeOnThisOrChildren(elt, "sse-connect").forEach(function(child) { | ||||
| 			var sseURL = api.getAttributeValue(child, "sse-connect"); | ||||
| 			if (sseURL == null) { | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			ensureEventSource(child, sseURL, retryCount); | ||||
| 		}); | ||||
|  | ||||
| 		// handle legacy sse, remove for HTMX2 | ||||
| 		queryAttributeOnThisOrChildren(elt, "hx-sse").forEach(function(child) { | ||||
| 			var sseURL = getLegacySSEURL(child); | ||||
| 			if (sseURL == null) { | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			ensureEventSource(child, sseURL, retryCount); | ||||
| 		}); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	function ensureEventSource(elt, url, retryCount) { | ||||
| 		var source = htmx.createEventSource(url); | ||||
|  | ||||
| 		source.onerror = function(err) { | ||||
|  | ||||
| 			// Log an error event | ||||
| 			api.triggerErrorEvent(elt, "htmx:sseError", { error: err, source: source }); | ||||
|  | ||||
| 			// If parent no longer exists in the document, then clean up this EventSource | ||||
| 			if (maybeCloseSSESource(elt)) { | ||||
| 				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() { | ||||
| 					ensureEventSourceOnElement(elt, Math.min(7, retryCount + 1)); | ||||
| 				}, timeout); | ||||
| 			} | ||||
| 		}; | ||||
|  | ||||
| 		source.onopen = function(evt) { | ||||
| 			api.triggerEvent(elt, "htmx:sseOpen", { source: source }); | ||||
| 		} | ||||
|  | ||||
| 		api.getInternalData(elt).sseEventSource = source; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * maybeCloseSSESource confirms that the parent element still exists. | ||||
| 	 * If not, then any associated SSE source is closed and the function returns true. | ||||
| 	 *  | ||||
| 	 * @param {HTMLElement} elt  | ||||
| 	 * @returns boolean | ||||
| 	 */ | ||||
| 	function maybeCloseSSESource(elt) { | ||||
| 		if (!api.bodyContains(elt)) { | ||||
| 			var source = api.getInternalData(elt).sseEventSource; | ||||
| 			if (source != undefined) { | ||||
| 				source.close(); | ||||
| 				// source = null | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * queryAttributeOnThisOrChildren returns all nodes that contain the requested attributeName, INCLUDING THE PROVIDED ROOT ELEMENT. | ||||
| 	 *  | ||||
| 	 * @param {HTMLElement} elt  | ||||
| 	 * @param {string} attributeName  | ||||
| 	 */ | ||||
| 	function queryAttributeOnThisOrChildren(elt, attributeName) { | ||||
|  | ||||
| 		var result = []; | ||||
|  | ||||
| 		// If the parent element also contains the requested attribute, then add it to the results too. | ||||
| 		if (api.hasAttribute(elt, attributeName)) { | ||||
| 			result.push(elt); | ||||
| 		} | ||||
|  | ||||
| 		// Search all child nodes that match the requested attribute | ||||
| 		elt.querySelectorAll("[" + attributeName + "], [data-" + attributeName + "]").forEach(function(node) { | ||||
| 			result.push(node); | ||||
| 		}); | ||||
|  | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param {HTMLElement} elt | ||||
| 	 * @param {string} content  | ||||
| 	 */ | ||||
| 	function swap(elt, content) { | ||||
|  | ||||
| 		api.withExtensions(elt, function(extension) { | ||||
| 			content = extension.transformResponse(content, null, elt); | ||||
| 		}); | ||||
|  | ||||
| 		var swapSpec = api.getSwapSpecification(elt); | ||||
| 		var target = api.getTarget(elt); | ||||
| 		var settleInfo = api.makeSettleInfo(elt); | ||||
|  | ||||
| 		api.selectAndSwap(swapSpec.swapStyle, target, elt, content, settleInfo); | ||||
|  | ||||
| 		settleInfo.elts.forEach(function(elt) { | ||||
| 			if (elt.classList) { | ||||
| 				elt.classList.add(htmx.config.settlingClass); | ||||
| 			} | ||||
| 			api.triggerEvent(elt, 'htmx:beforeSettle'); | ||||
| 		}); | ||||
|  | ||||
| 		// Handle settle tasks (with delay if requested) | ||||
| 		if (swapSpec.settleDelay > 0) { | ||||
| 			setTimeout(doSettle(settleInfo), swapSpec.settleDelay); | ||||
| 		} else { | ||||
| 			doSettle(settleInfo)(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * doSettle mirrors much of the functionality in htmx that  | ||||
| 	 * settles elements after their content has been swapped. | ||||
| 	 * TODO: this should be published by htmx, and not duplicated here | ||||
| 	 * @param {import("../htmx").HtmxSettleInfo} settleInfo  | ||||
| 	 * @returns () => void | ||||
| 	 */ | ||||
| 	function doSettle(settleInfo) { | ||||
|  | ||||
| 		return function() { | ||||
| 			settleInfo.tasks.forEach(function(task) { | ||||
| 				task.call(); | ||||
| 			}); | ||||
|  | ||||
| 			settleInfo.elts.forEach(function(elt) { | ||||
| 				if (elt.classList) { | ||||
| 					elt.classList.remove(htmx.config.settlingClass); | ||||
| 				} | ||||
| 				api.triggerEvent(elt, 'htmx:afterSettle'); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function hasEventSource(node) { | ||||
| 		return api.getInternalData(node).sseEventSource != null; | ||||
| 	} | ||||
|  | ||||
| })(); | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder