From 457da4b34ed330edd641d56ea7d85a12f2099cf9 Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 19 Oct 2024 09:14:27 +0200 Subject: [PATCH] Avoid duplicate core extensions doc (#102) * Remove www * Link to main website/repo docs in each core extension's README * Redirect --- src/head-support/README.md | 110 +------- src/htmx-1-compat/README.md | 26 +- src/preload/README.md | 102 +------ src/response-targets/README.md | 139 +--------- src/sse/README.md | 188 +------------ src/ws/README.md | 250 +---------------- www/_hyperscript.min.js | 1 - www/_redirects | 1 + www/index.html | 475 --------------------------------- 9 files changed, 7 insertions(+), 1285 deletions(-) delete mode 100644 www/_hyperscript.min.js create mode 100644 www/_redirects delete mode 100644 www/index.html diff --git a/src/head-support/README.md b/src/head-support/README.md index 7560f91..796f2b5 100644 --- a/src/head-support/README.md +++ b/src/head-support/README.md @@ -1,109 +1 @@ - -The `head-support` extension adds support for [head tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) -in responses to htmx requests. - -htmx began as a library focused on _partial replacement_ of HTML within the `body` tag. As such, merging additional -information such as the head tag was not a focus of the library. (This is in contrast with, for example, TurboLinks, -which was focused on merging entire web pages retrieved via AJAX into the browser.) - -The [`hx-boost`](@/attributes/hx-boost.md) attribute moved htmx closer to this world of full HTML-document support & -support for extracting the `title` tag out of head elements was eventually added, but full head tag support has never been -a feature of the library. This extension addresses that shortcoming. - -## Install - -```html - -``` - -## Usage - -```html - - ... -``` - -With this installed, all responses that htmx receives that contain a `head` tag in them (even if they are not complete -HTML documents with a root `` element) will be processed. - -How the head tag is handled depends on the type of htmx request. - -If the htmx request is from a boosted element, then the following merge algorithm is used: - -* Elements that exist in the current head as exact textual matches will be left in place -* Elements that do not exist in the current head will be added at the end of the head tag -* Elements that exist in the current head, but not in the new head will be removed from the head - -If the htmx request is from a non-boosted element, then all content will be _appended_ to the existing head element. - -If you wish to override this behavior in either case, you can place the `hx-head` attribute on the new `` tag, -with either of the following two values: - -* `merge` - follow the merging algorithm outlined above -* `append` - append the elements to the existing head - -### Controlling Merge Behavior - -Beyond this, you may also control merging behavior of individual elements with the following attributes: - -* If you place `hx-head="re-eval"` on a head element, it will be re-added (removed and appended) to the head tag on every - request, even if it already exists. This can be useful to execute a script on every htmx request, for example. -* If you place `hx-preserve="true"` on an element, it will never be removed from the head - -### Example - -As an example, consider the following head tag in an existing document: - -```html - - - - - - -``` - -If htmx receives a request containing this new head tag: - -```html - - - - - - -``` - -Then the following operations will occur: - -* `` will be left alone -* `` will be removed from the head -* `` will be added to the head -* `` will be removed from the head -* `` will be left alone -* `` will be added to the head - -The final head element will look like this: - -```html - - - - - - -``` - -## Events - -This extension triggers the following events: - -* `htmx:removingHeadElement` - triggered when a head element is about to be removed, with the element being removed - available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be removed. -* `htmx:addingHeadElement` - triggered when a head element is about to be added, with the element being added - available in `event.detail.headElement`. If `preventDefault()` is invoked on the event, the element will not be added. -* `htmx:afterHeadMerge` - triggered after a head tag merge has occurred, with the following values available in the event `detail`: - * `added` - added head elements - * `kept` - kept head elements - * `removed` - removed head elements -* `htmx:beforeHeadMerge` - triggered before a head merge occurs +See https://htmx.org/extensions/head-support/, or https://github.com/bigskysoftware/htmx/blob/master/www/content/extensions/head-support.md \ No newline at end of file diff --git a/src/htmx-1-compat/README.md b/src/htmx-1-compat/README.md index a35a84e..47c80f9 100644 --- a/src/htmx-1-compat/README.md +++ b/src/htmx-1-compat/README.md @@ -1,25 +1 @@ -The `htmx-1-compat` extension allows you to almost seamlessly upgrade from htmx 1.x to htmx 2. - -## Install - -```html - -``` - -## What it covers -Htmx 2 introduced a few [breaking changes](https://v2-0v2-0.htmx.org/migration-guide-htmx-1/). - -To make upgrading from htmx 1.x to htmx 2 easier, we're providing this extension that reverts most of those, so you're able to benefit from the other changes without breaking your application. - -### Obsolete attributes -- htmx 2 removed the deprecated [hx-ws](https://htmx.org/attributes/hx-ws/) and [hx-sse](https://htmx.org/attributes/hx-sse/) attributes, that this extension restores. -- htmx 2 removed the deprecated `hx-on` attribute in favor of the wildcard [`hx-on*` attribute](https://htmx.org/attributes/hx-on/), that this extension restores. - -### Default Changes -- reverts [htmx.config.scrollBehavior](https://htmx.org/reference/#config) to 'smooth'. -- makes `DELETE` requests use a form-encoded body rather than URL parameters (htmx 2 uses URL parameters for `DELETE` as default as per [the spec](https://www.rfc-editor.org/rfc/rfc9110.html#name-delete)). -- allows cross-domain requests by default (htmx 2 now forbids it by default). - -## What it does not cover -- IE11 support was dropped in htmx 2, and this extension cannot revert that. If you need IE11 support, please stay with htmx 1 that will continue being supported. -- htmx 2 introduced the breaking change that is the [swap method](https://v2-0v2-0.htmx.org/api/#swap) to the extensions API. If you were only using core extensions, then you shouldn't need any additional work. If you were using custom or community extensions, make sure that they were updated to work with htmx 2's API. +See https://htmx.org/extensions/htmx-1-compat/, or https://github.com/bigskysoftware/htmx/blob/master/www/content/extensions/htmx-1-compat.md \ No newline at end of file diff --git a/src/preload/README.md b/src/preload/README.md index f5b8deb..489f133 100644 --- a/src/preload/README.md +++ b/src/preload/README.md @@ -1,101 +1 @@ - -The `preload` extension allows you to load HTML fragments into your browser's cache before they are requested by the user, so that additional pages appear to users to load nearly instantaneously. As a developer, you can customize its behavior to fit your applications needs and use cases. - -**IMPORTANT:** Preloading content judiciously can improve your web application's perceived performance, but preloading too many resources can negatively impact your visitors' bandwidth and your server performance by initiating too many unused requests. Use this extension carefully! - -## Install - -```html - -``` - -## Usage - -Register the extension with htmx using the `hx-ext` attribute. Then, add a `preload` attribute to any hyperlinks and `hx-get` elements you want to preload. By default, resources will be loaded as soon as the `mousedown` event begins, giving your application a roughly 100-200ms head start on serving responses. See configuration below for other options. - -```html - -

What Works

- WILL BE requested using a standard XMLHttpRequest() and default options (below) - - -

What WILL NOT WORK

- WILL NOT be preloaded because it does not have an explicit "preload" attribute - WILL NOT be preloaded because it is an HX-POST transaction. - -``` - -### Inheriting Preload Settings - -You can add the `preload` attribute to the top-level element that contains several `` or `hx-get=""` elements, and all of them will be preloaded. Be careful with this setting, because you can end up wasting bandwidth if you preload many more resources than you need. - -```html - - - -``` - -### Preloading of Linked Images - -After an HTML page (or page fragment) is preloaded, this extension can also preload linked image resources. It will not load or run linked Javascript or Cascading Stylesheet content, whether linked or embedded in the preloaded HTML. To preload images as well, use the following syntax. - -```html -
- Next Page -
-``` - -### Configuration - -Defaults for this extension are chosen to balance users' perceived performance with potential load on your servers from unused requests. As a developer, you can modify two settings to customize this behavior to your specific use cases. - -#### preload="mousedown" (DEFAULT) - -The default behavior for this extension is to begin loading a resource when the user presses the mouse down. This is a conservative setting that guarantees the user actually intends to use the linked resource. Because user click events typically take 100-200ms to complete, this setting gives your server a significant headstart compared with a regular click. - -```html -This will be preloaded when the user begins to click. -``` - -#### preload="mouseover" - -To preload links more aggressively, you can trigger the preload to happen when the user's mouse hovers over the link instead. To prevent many resources from being loaded when the user scrolls or moves the mouse across a large list of objects, a 100ms delay is built in to this action. If the user's mouse leaves the element *before* this timeout expires, then the resource is not preloaded. - -Typical users hover over links for several hundred milliseconds before they click, which gives your server even more time to respond to the request than the `mousedown` option above. [Test your own hover timing here.](http://instantclick.io/click-test). However, be careful when using this option because it can increase server load by requesting resources unnecessarily. - -```html -This will be preloaded when the user's mouse remains over it for more than 100ms. -``` - -#### preload="custom-event-name" - -Preload can also listen to any custom event within the system, triggering resources to be preloaded (if they have not already been cached by the browser). The extension itself generates an event called `preload:init` that can be used to trigger preloads as soon as an object has been processed by htmx. - -```html - - -
-
- -``` - -* The response from the `/register` URL will replace contents of the `div` with the - `id` `response-div` when response code is 200 (OK). - -* The response from the `/register` URL will replace contents of the `div` with the `id` - `serious-errors` when response code begins with a digit 5 (server errors). - -* The response from the `/register` URL will replace contents of the `div` with - the `id` `not-found` when response code is 404 (Not Found). - -Sometimes you may not want to handle 5xx and 4xx errors separately, in which case you -can use `hx-target-error`: - -```html -
-
- -
-
-``` - -2xx codes will be handled as in the previous example. However, when the response code is 5xx -or 4xx, the response from `/register` will replace the contents of the `div` with the `id` -`any-errors`. - -## Wildcard resolution - -When status response code does not match existing `hx-target-[CODE]` attribute name -then its numeric part expressed as a string is trimmed with last character being -replaced with the asterisk (`*`). This lookup process continues until the attribute -is found or there are no more digits. - -For example, if a browser receives 404 error code, the following attribute names will -be looked up (in the given order): - -* `hx-target-404` -* `hx-target-40*` -* `hx-target-4*` -* `hx-target-*`. - - -_If you are using tools that do not support asterisks in HTML attributes, you -may instead use the `x` character, e.g., `hx-target-4xx`._ - -## Notes - -* `hx-target-…` is inherited and can be placed on a parent element. -* `hx-target-…` cannot be used to handle HTTP response code 200. -* `hx-target-…` will honor `HX-Retarget` by default and will prefer it over any - calculated target but it can be changed by disabling the - `htmx.config.responseTargetPrefersRetargetHeader` configuration option. -* To avoid surprises the `hx-ext` attribute used to enable this extension should be - placed on a parent element containing elements with `hx-target-…` and `hx-target` - attributes. - -## See also - -* [`hx-target`](https://htmx.org/reference/hx-target.md), specifies the target element to be swapped +See https://htmx.org/extensions/response-targets, or https://github.com/bigskysoftware/htmx/blob/master/www/content/extensions/response-targets.md \ No newline at end of file diff --git a/src/sse/README.md b/src/sse/README.md index b46b8d3..e7f38c6 100644 --- a/src/sse/README.md +++ b/src/sse/README.md @@ -1,187 +1 @@ -The `Server Sent Events` extension connects to an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) directly from HTML. It manages the connections to your web server, listens for server events, and then swaps their contents into your htmx webpage in real-time. - -SSE is a lightweight alternative to WebSockets that works over existing HTTP connections, so it is easy to use through proxy servers and firewalls. Remember, SSE is a uni-directional service, so you cannot send any messages to an SSE server once the connection has been established. If you need bi-directional communication, then you should consider using [WebSockets](@/web-sockets.md) instead. - -This extension replaces the experimental `hx-sse` attribute built into previous versions of htmx. For help migrating from older versions, see the migration guide at the bottom of this page. - -Use the following attributes to configure how SSE connections behave: - -* `sse-connect=""` - The URL of the SSE server. -* `sse-swap=""` - The name of the message to swap into the DOM. -* `hx-trigger="sse:"` - SSE messages can also trigger HTTP callbacks using the [`hx-trigger`](https://htmx.org/reference/hx-trigger.md) attribute. -* `sse-close=` - To close the EventStream gracefully when that message is received. This might be helpful if you want to send information to a client that will eventually stop. - -## Install - -```html - -``` - -## Usage - -```html -
- Contents of this box will be updated in real time - with every SSE message received from the chatroom. -
-``` - -### Connecting to an SSE Server - -To connect to an SSE server, use the `hx-ext="sse"` attribute to install the extension on that HTML element, then add `sse-connect=""` to the element to make the connection. - -When designing your server application, remember that SSE works just like any HTTP request. Although you cannot send any messages to the server after you have established a connection, you can send parameters to the server along with your request. So, instead of making an SSE connection to your server at `https://my-server/chat-updates` you can also connect to `https://my-server/chat-updates?friends=true&format=detailed`. This allows your server to customize its responses to what your client needs. - -### Receiving Named Events - -SSE messages consist of an event name and a data packet. No other metadata is allowed in the message. Here is an example: - -```txt -event: EventName -data:
Content to swap into your HTML page.
-``` - -We'll use the `sse-swap` attribute to listen for this event and swap its contents into our webpage. - -```html -
-``` - -Notice that the name `EventName` from the server's message must match the value in the `sse-swap` attribute. Your server can use as many different event names as necessary, but be careful: browsers can only listen for events that have been explicitly named. So, if your server sends an event named `ChatroomUpdate` but your browser is only listening for events named `ChatUpdate` then the extra event will be discarded. - -### Receiving Unnamed Events - -SSE messages can also be sent without any event name. In this case, the browser uses the default name `message` in its place. The same rules specified above still apply. If your server sends an unnamed message, then you must listen for it by including `sse-swap="message"`. There is no option for using a catch-all name. Here's how this looks: - -```txt -data:
Content to swap into your HTML page.
-``` - -```html -
-``` - -### Receiving Multiple Events - -You can also listen to multiple events (named or unnamed) from a single EventSource. Listeners must be either 1) the same element that contains the `hx-ext` and `sse-connect` attributes, or 2) child elements of the element containing the `hx-ext` and `sse-connect` attributes. - -```html - -Multiple events in the same element -
- -Multiple events in different elements (from the same source). -
-
-
-
-``` - -### Trigger Server Callbacks - -When a connection for server sent events has been established, child elements can listen for these events by using the special [`hx-trigger`](https://htmx.org/reference/hx-trigger.md) syntax `sse:`. This, when combined with an `hx-get` or similar will trigger the element to make a request. - -Here is an example: - -```html -
-
- ... -
-
-``` - -This example establishes an SSE connection to the `event_stream` end point which then triggers -a `GET` to the `/chatroom` url whenever the `chatter` event is seen. - -### Automatic Reconnection - -If the SSE Event Stream is closed unexpectedly, browsers are supposed to attempt to reconnect automatically. However, in rare situations this does not work and your browser can be left hanging. This extension adds its own reconnection logic (using an [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff)) on top of the browser's automatic reconnection, so that your SSE streams will always be as reliable as possible. - -### Testing SSE Connections with the Demo Server - -Htmx includes a demo SSE server written in Node.js that will help you to see SSE in action, and begin bootstrapping your own SSE code. It is located in the /test/ws-sse folder of the htmx distribution. Look at /test/ws-sse/README.md for instructions on running and using the test server. - -### Migrating from Previous Versions - -Previous versions of htmx used a built-in tag `hx-sse` to implement Server Sent Events. This code has been migrated into an extension instead. Here are the steps you need to take to migrate to this version: - -| Old Attribute | New Attribute | Comments | -|--------------------------------|--------------------------|------------------| -| `hx-sse=""` | `hx-ext="sse"` | Use the `hx-ext="sse"` attribute to install the SSE extension into any HTML element. | -| `hx-sse="connect:"` | `sse-connect=""` | Add a new attribute `sse-connect` to the tag that specifies the URL of the Event Stream. This attribute must be in the same tag as the `hx-ext` attribute. | -| `hx-sse="swap:"` | `sse-swap=""` | Add a new attribute `sse-swap` to any elements that will be swapped in via the SSE extension. This attribute must be placed **on** or **inside of** the tag containing the `hx-ext` attribute. | -| `hx-trigger="sse:"` | NO CHANGE | any `hx-trigger` attributes do not need to change. The extension will identify these attributes and add listeners for any events prefixed with `sse:` | - -### Listening to events dispatched by this extension - -This extension dispatches several events. You can listen for these events like so: - -```javascript -document.body.addEventListener('htmx:sseBeforeMessage', function (e) { - // do something before the event data is swapped in -}) -``` - -Each event object has a `detail` field that contains details of the event. - -#### `htmx:sseOpen` - -This event is dispatched when an SSE connection has been successfully established. - -##### Details - -* `detail.elt` - The element on which the SSE connection was setup. This is the element which has the `sse-connect` attribute. -* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) object. - -#### `htmx:sseError` - -This event is dispatched when an SSE connection could not be established. - -##### Details - -* `detail.error` - The error that occured while creating an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). -* `detail.source` - The [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). - -#### `htmx:sseBeforeMessage` - -This event is dispatched just before the SSE event data is swapped into the DOM. If you don't want to swap call `preventDefault()` on the event. Additionally the `detail` field is a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message. - -##### Details - -* `detail.elt` - The swap target. - -#### `htmx:sseMessage` - -This event is dispatched after the SSE event data has been swapped into the DOM. The `detail` field is a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message. - -#### `htmx:sseClose` - -This event is dispatched in three different closing scenario. To control for the scenario the user can control for the evt.detail.sseclose property. - -```javascript -document.body.addEventListener('htmx:sseClose', function (e) { - const reason = e.detail.type - switch(reason) { - case "nodeMissing": - // Parent node is missing and therefore connection was closed - ... - case "nodeReplaced": - // Parent node replacement caused closing of connection - ... - case "message": - // connection was closed due to reception of message sse-close - ... - } -}) -``` - -##### Details - -* `detail.elt` - The swap target. - -### Additional SSE Resources - -* [Wikipedia](https://en.wikipedia.org/wiki/Server-sent_events) -* [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) -* [Can I Use?](https://caniuse.com/eventsource) +See https://htmx.org/extensions/sse, or https://github.com/bigskysoftware/htmx/blob/master/www/content/extensions/sse.md \ No newline at end of file diff --git a/src/ws/README.md b/src/ws/README.md index d620ebf..f5c5b45 100644 --- a/src/ws/README.md +++ b/src/ws/README.md @@ -1,249 +1 @@ -The `WebSockets` extension enables easy, bi-directional communication -with [Web Sockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications) -servers directly from HTML. This replaces the experimental `hx-ws` attribute built into previous versions of htmx. For -help migrating from older versions, see the [Migrating](#migrating-from-previous-versions) guide at the bottom of this page. - -Use the following attributes to configure how WebSockets behave: - -* `ws-connect=""` or `ws-connect=":"` - A URL to establish a `WebSocket` connection against. -* Prefixes `ws` or `wss` can optionally be specified. If not specified, HTMX defaults to adding the location's scheme-type, - host and port to have browsers send cookies via websockets. -* `ws-send` - Sends a message to the nearest websocket based on the trigger value for the element (either the natural - event - or the event specified by [`hx-trigger`]) - -## Install - -```html - -``` - -## Usage - -```html - -
-
-
- ... -
-
- -
-
-``` - -### Configuration - -WebSockets extension support two configuration options: - -- `createWebSocket` - a factory function that can be used to create a custom WebSocket instances. Must be a function, - returning `WebSocket` object -- `wsBinaryType` - a string value, that defines - socket's [`binaryType`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/binaryType) property. Default value - is `blob` - -### Receiving Messages from a WebSocket - -The example above establishes a WebSocket to the `/chatroom` end point. Content that is sent down from the websocket -will -be parsed as HTML and swapped in by the `id` property, using the same logic -as [Out of Band Swaps](https://htmx.org/attributes/hx-swap-oob/). - -As such, if you want to change the swapping method (e.g., append content at the end of an element or delegate swapping -to an extension), -you need to specify that in the message body, sent by the server. - -```html - -
- ... -
- -
- New message received -
- -
- .... -
-``` - -### Sending Messages to a WebSocket - -In the example above, the form uses the `ws-send` attribute to indicate that when it is submitted, the form values -should be **serialized as JSON** -and send to the nearest enclosing `WebSocket`, in this case the `/chatroom` endpoint. - -The serialized values will include a field, `HEADERS`, that includes the headers normally submitted with an htmx -request. - -### Automatic Reconnection - -If the WebSocket is closed unexpectedly, due to `Abnormal Closure`, `Service Restart` or `Try Again Later`, this -extension will attempt to reconnect until the connection is reestablished. - -By default, the extension uses a -full-jitter [exponential-backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) that chooses a randomized -retry delay that grows exponentially over time. You can use a different algorithm by writing it -into `htmx.config.wsReconnectDelay`. This function takes a single parameter, the number of retries, and returns the -time (in milliseconds) to wait before trying again. - -```javascript -// example reconnect delay that you shouldn't use because -// it's not as good as the algorithm that's already in place -htmx.config.wsReconnectDelay = function (retryCount) { - return retryCount * 1000 // return value in milliseconds -} -``` - -The extension also implements a simple queuing mechanism that keeps messages in memory when the socket is not in `OPEN` -state and sends them once the connection is restored. - -### Events - -WebSockets extensions exposes a set of events that allow you to observe and customize its behavior. - -#### Event - `htmx:wsConnecting` {#htmx:wsConnecting} - -This event is triggered when a connection to a WebSocket endpoint is being attempted. - -##### Details - -* `detail.event.type` - the type of the event (`'connecting'`) - -#### Event - `htmx:wsOpen` {#htmx:wsOpen} - -This event is triggered when a connection to a WebSocket endpoint has been established. - -##### Details - -* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute) -* `detail.event` - the original event from the socket -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsClose` {#htmx:wsClose} - -This event is triggered when a connection to a WebSocket endpoint has been closed normally. -You can check if the event was caused by an error by inspecting `detail.event` property. - -##### Details - -* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute) -* `detail.event` - the original event from the socket -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsError` {#htmx:wsError} - -This event is triggered when `onerror` event on a socket is raised. - -##### Details - -* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute) -* `detail.error` - the error object -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsBeforeMessage` {#htmx:wsBeforeMessage} - -This event is triggered when a message has just been received by a socket, similar to `htmx:beforeOnLoad`. This event -fires -before any processing occurs. - -If the event is cancelled, no further processing will occur. - -* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute) -* `detail.message` - raw message content -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsAfterMessage` {#htmx:wsAfterMessage} - -This event is triggered when a message has been completely processed by htmx and all changes have been -settled, similar to `htmx:afterOnLoad`. - -Cancelling this event has no effect. - -* `detail.elt` - the element that holds the socket (the one with `ws-connect` attribute) -* `detail.message` - raw message content -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsConfigSend` {#htmx:wsConfigSend} - -This event is triggered when preparing to send a message from `ws-send` element. -Similarly to [`htmx:configRequest`](https://htmx.org/events#htmx:configRequest), it allows you to modify the message -before sending. - -If the event is cancelled, no further processing will occur and no messages will be sent. - -##### Details - -* `detail.parameters` - the parameters that will be submitted in the request -* `detail.unfilteredParameters` - the parameters that were found before filtering - by [`hx-select`](https://htmx.org/reference/hx-select.md) -* `detail.headers` - the request headers. Will be attached to the body in `HEADERS` property, if not falsy -* `detail.errors` - validation errors. Will prevent sending and - trigger [`htmx:validation:halted`](https://htmx.org/events#htmx:validation:halted) event if not empty -* `detail.triggeringEvent` - the event that triggered sending -* `detail.messageBody` - raw message body that will be sent to the socket. Undefined, can be set to value of any type, - supported by WebSockets. If set, will override - default JSON serialization. Useful, if you want to use some other format, like XML or MessagePack -* `detail.elt` - the element that dispatched the sending (the one with `ws-send` attribute) -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsBeforeSend` {#htmx:wsBeforeSend} - -This event is triggered just before sending a message. This includes messages from the queue. -Message can not be modified at this point. - -If the event is cancelled, the message will be discarded from the queue and not sent. - -##### Details - -* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute) -* `detail.message` - the raw message content -* `detail.socketWrapper` - the wrapper around socket object - -#### Event - `htmx:wsAfterSend` {#htmx:wsAfterSend} - -This event is triggered just after sending a message. This includes messages from the queue. - -Cancelling the event has no effect. - -##### Details - -* `detail.elt` - the element that dispatched the request (the one with `ws-connect` attribute) -* `detail.message` - the raw message content -* `detail.socketWrapper` - the wrapper around socket object - -#### Socket wrapper - -You may notice that all events expose `detail.socketWrapper` property. This wrapper holds the socket -object itself and the message queue. It also encapsulates reconnection algorithm. It exposes a few members: - -- `send(message, fromElt)` - sends a message safely. If the socket is not open, the message will be persisted in the - queue - instead and sent when the socket is ready. -- `sendImmediately(message, fromElt)` - attempts to send a message regardless of socket state, bypassing the queue. May - fail -- `queue` - an array of messages, awaiting in the queue. - -This wrapper can be used in your event handlers to monitor and manipulate the queue (e.g., you can reset the queue when -reconnecting), and to send additional messages (e.g., if you want to send data in batches). -The `fromElt` parameter is optional and, when specified, will trigger corresponding websocket events from -specified element, namely `htmx:wsBeforeSend` and `htmx:wsAfterSend` events when sending your messages. - -### Testing with the Demo Server - -Htmx includes a demo WebSockets server written in Node.js that will help you to see WebSockets in action, and begin -bootstrapping your own WebSockets code. It is located in the /test/ws-sse folder of the htmx distribution. Look at -/test/ws-sse/README.md for instructions on running and using the test server. - -### Migrating from Previous Versions - -Previous versions of htmx used a built-in tag `hx-ws` to implement WebSockets. This code has been migrated into an -extension instead. Here are the steps you need to take to migrate to this version: - -| Old Attribute | New Attribute | Comments | -|-------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------------| -| `hx-ws=""` | `hx-ext="ws"` | Use the `hx-ext="ws"` attribute to install the WebSockets extension into any HTML element. | -| `hx-ws="connect:"` | `ws-connect=""` | Add a new attribute `ws-connect` to the tag that defines the extension to specify the URL of the WebSockets server you're using. | -| `hx-ws="send"` | `ws-send=""` | Add a new attribute `ws-send` to mark any child forms that should send data to your WebSocket server | +See https://htmx.org/extensions/ws, or https://github.com/bigskysoftware/htmx/blob/master/www/content/extensions/ws.md \ No newline at end of file diff --git a/www/_hyperscript.min.js b/www/_hyperscript.min.js deleted file mode 100644 index 355c78a..0000000 --- a/www/_hyperscript.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(e,t){const r=t(e);if(typeof exports==="object"&&typeof exports["nodeName"]!=="string"){module.exports=r}else{e["_hyperscript"]=r;if("document"in e)e["_hyperscript"].browserInit()}})(typeof self!=="undefined"?self:this,(e=>{"use strict";const t={dynamicResolvers:[function(e,t){if(e==="Fixed"){return Number(t).toFixed()}else if(e.indexOf("Fixed:")===0){let r=e.split(":")[1];return Number(t).toFixed(parseInt(r))}}],String:function(e){if(e.toString){return e.toString()}else{return""+e}},Int:function(e){return parseInt(e)},Float:function(e){return parseFloat(e)},Number:function(e){return Number(e)},Date:function(e){return new Date(e)},Array:function(e){return Array.from(e)},JSON:function(e){return JSON.stringify(e)},Object:function(e){if(e instanceof String){e=e.toString()}if(typeof e==="string"){return JSON.parse(e)}else{return Object.assign({},e)}}};const r={attributes:"_, script, data-script",defaultTransition:"all 500ms ease-in",disableSelector:"[disable-scripting], [data-disable-scripting]",hideShowStrategies:{},conversions:t};class n{static OP_TABLE={"+":"PLUS","-":"MINUS","*":"MULTIPLY","/":"DIVIDE",".":"PERIOD","..":"ELLIPSIS","\\":"BACKSLASH",":":"COLON","%":"PERCENT","|":"PIPE","!":"EXCLAMATION","?":"QUESTION","#":"POUND","&":"AMPERSAND",$:"DOLLAR",";":"SEMI",",":"COMMA","(":"L_PAREN",")":"R_PAREN","<":"L_ANG",">":"R_ANG","<=":"LTE_ANG",">=":"GTE_ANG","==":"EQ","===":"EQQ","!=":"NEQ","!==":"NEQQ","{":"L_BRACE","}":"R_BRACE","[":"L_BRACKET","]":"R_BRACKET","=":"EQUALS"};static isValidCSSClassChar(e){return n.isAlpha(e)||n.isNumeric(e)||e==="-"||e==="_"||e===":"}static isValidCSSIDChar(e){return n.isAlpha(e)||n.isNumeric(e)||e==="-"||e==="_"||e===":"}static isWhitespace(e){return e===" "||e==="\t"||n.isNewline(e)}static positionString(e){return"[Line: "+e.line+", Column: "+e.column+"]"}static isNewline(e){return e==="\r"||e==="\n"}static isNumeric(e){return e>="0"&&e<="9"}static isAlpha(e){return e>="a"&&e<="z"||e>="A"&&e<="Z"}static isIdentifierChar(e,t){return e==="_"||e==="$"}static isReservedChar(e){return e==="`"||e==="^"}static isValidSingleQuoteStringStart(e){if(e.length>0){var t=e[e.length-1];if(t.type==="IDENTIFIER"||t.type==="CLASS_REF"||t.type==="ID_REF"){return false}if(t.op&&(t.value===">"||t.value===")")){return false}}return true}static tokenize(e,t){var r=[];var a=e;var o=0;var s=0;var u=1;var l="";var c=0;function f(){return t&&c===0}while(o=0){return this.consumeToken()}}requireToken(e,t){var r=this.matchToken(e,t);if(r){return r}else{this.raiseError(this,"Expected '"+e+"' but found '"+this.currentToken().value+"'")}}peekToken(e,t,r){t=t||0;r=r||"IDENTIFIER";if(this.tokens[t]&&this.tokens[t].value===e&&this.tokens[t].type===r){return this.tokens[t]}}matchToken(e,t){if(this.follows.indexOf(e)!==-1){return}t=t||"IDENTIFIER";if(this.currentToken()&&this.currentToken().value===e&&this.currentToken().type===t){return this.consumeToken()}}consumeToken(){var e=this.tokens.shift();this.consumed.push(e);this._lastConsumed=e;this.consumeWhitespace();return e}consumeUntil(e,t){var r=[];var n=this.token(0,true);while((t==null||n.type!==t)&&(e==null||n.value!==e)&&n.type!=="EOF"){var i=this.tokens.shift();this.consumed.push(i);r.push(n);n=this.token(0,true)}this.consumeWhitespace();return r}lastWhitespace(){if(this.consumed[this.consumed.length-1]&&this.consumed[this.consumed.length-1].type==="WHITESPACE"){return this.consumed[this.consumed.length-1].value}else{return""}}consumeUntilWhitespace(){return this.consumeUntil(null,"WHITESPACE")}hasMore(){return this.tokens.length>0}token(e,t){var r;var n=0;do{if(!t){while(this.tokens[n]&&this.tokens[n].type==="WHITESPACE"){n++}}r=this.tokens[n];e--;n++}while(e>-1);if(r){return r}else{return{type:"EOF",value:"<<>>"}}}currentToken(){return this.token(0)}lastMatch(){return this._lastConsumed}static sourceFor=function(){return this.programSource.substring(this.startToken.start,this.endToken.end)};static lineFor=function(){return this.programSource.split("\n")[this.startToken.line-1]};follows=[];pushFollow(e){this.follows.push(e)}popFollow(){this.follows.pop()}clearFollows(){var e=this.follows;this.follows=[];return e}restoreFollows(e){this.follows=e}}class a{constructor(e){this.runtime=e;this.possessivesDisabled=false;this.addGrammarElement("feature",(function(e,t,r){if(r.matchOpToken("(")){var n=e.requireElement("feature",r);r.requireOpToken(")");return n}var i=e.FEATURES[r.currentToken().value||""];if(i){return i(e,t,r)}}));this.addGrammarElement("command",(function(e,t,r){if(r.matchOpToken("(")){const t=e.requireElement("command",r);r.requireOpToken(")");return t}var n=e.COMMANDS[r.currentToken().value||""];let i;if(n){i=n(e,t,r)}else if(r.currentToken().type==="IDENTIFIER"){i=e.parseElement("pseudoCommand",r)}if(i){return e.parseElement("indirectStatement",r,i)}return i}));this.addGrammarElement("commandList",(function(e,t,r){if(r.hasMore()){var n=e.parseElement("command",r);if(n){r.matchToken("then");const t=e.parseElement("commandList",r);if(t)n.next=t;return n}}return{type:"emptyCommandListCommand",op:function(e){return t.findNext(this,e)},execute:function(e){return t.unifiedExec(this,e)}}}));this.addGrammarElement("leaf",(function(e,t,r){var n=e.parseAnyOf(e.LEAF_EXPRESSIONS,r);if(n==null){return e.parseElement("symbol",r)}return n}));this.addGrammarElement("indirectExpression",(function(e,t,r,n){for(var i=0;i{this.unifiedExec(e,t)})).catch((e=>{this.unifiedExec({op:function(){throw e}},t)}));return}else if(r===o.HALT){if(t.meta.finallyHandler&&!t.meta.handlingFinally){t.meta.handlingFinally=true;e=t.meta.finallyHandler}else{if(t.meta.onHalt){t.meta.onHalt()}if(t.meta.currentException){if(t.meta.reject){t.meta.reject(t.meta.currentException);return}else{throw t.meta.currentException}}else{return}}}else{e=r}}}unifiedEval(e,t){var r=[t];var n=false;var i=false;if(e.args){for(var a=0;a{r=this.wrapArrays(r);Promise.all(r).then((function(r){if(i){this.unwrapAsyncs(r)}try{var a=e.op.apply(e,r);t(a)}catch(e){n(e)}})).catch((function(e){n(e)}))}))}else{if(i){this.unwrapAsyncs(r)}return e.op.apply(e,r)}}_scriptAttrs=null;getScriptAttributes(){if(this._scriptAttrs==null){this._scriptAttrs=r.attributes.replace(/ /g,"").split(",")}return this._scriptAttrs}getScript(e){for(var t=0;t{this.initElement(e,e instanceof HTMLScriptElement&&e.type==="text/hyperscript"?document.body:e)}))}}initElement(e,t){if(e.closest&&e.closest(r.disableSelector)){return}var n=this.getInternalData(e);if(!n.initialized){var i=this.getScript(e);if(i){try{n.initialized=true;n.script=i;const r=this.lexer,s=this.parser;var a=r.tokenize(i);var o=s.parseHyperScript(a);if(!o)return;o.apply(t||e,e);setTimeout((()=>{this.triggerEvent(t||e,"load",{hyperscript:true})}),1)}catch(t){this.triggerEvent(e,"exception",{error:t});console.error("hyperscript errors were found on the following element:",e,"\n\n",t.message,t.stack)}}}}internalDataMap=new WeakMap;getInternalData(e){var t=this.internalDataMap.get(e);if(typeof t==="undefined"){this.internalDataMap.set(e,t={})}return t}typeCheck(e,t,r){if(e==null&&r){return true}var n=Object.prototype.toString.call(e).slice(8,-1);return n===t}getElementScope(e){var t=e.meta&&e.meta.owner;if(t){var r=this.getInternalData(t);var n="elementScope";if(e.meta.feature&&e.meta.feature.behavior){n=e.meta.feature.behavior+"Scope"}var i=h(r,n);return i}else{return{}}}isReservedWord(e){return["meta","it","result","locals","event","target","detail","sender","body"].includes(e)}isHyperscriptContext(e){return e instanceof f}resolveSymbol(t,r,n){if(t==="me"||t==="my"||t==="I"){return r.me}if(t==="it"||t==="its"||t==="result"){return r.result}if(t==="you"||t==="your"||t==="yourself"){return r.you}else{if(n==="global"){return e[t]}else if(n==="element"){var i=this.getElementScope(r);return i[t]}else if(n==="local"){return r.locals[t]}else{if(r.meta&&r.meta.context){var a=r.meta.context[t];if(typeof a!=="undefined"){return a}if(r.meta.context.detail){a=r.meta.context.detail[t];if(typeof a!=="undefined"){return a}}}if(this.isHyperscriptContext(r)&&!this.isReservedWord(t)){var o=r.locals[t]}else{var o=r[t]}if(typeof o!=="undefined"){return o}else{var i=this.getElementScope(r);o=i[t];if(typeof o!=="undefined"){return o}else{return e[t]}}}}}setSymbol(t,r,n,i){if(n==="global"){e[t]=i}else if(n==="element"){var a=this.getElementScope(r);a[t]=i}else if(n==="local"){r.locals[t]=i}else{if(this.isHyperscriptContext(r)&&!this.isReservedWord(t)&&typeof r.locals[t]!=="undefined"){r.locals[t]=i}else{var a=this.getElementScope(r);var o=a[t];if(typeof o!=="undefined"){a[t]=i}else{if(this.isHyperscriptContext(r)&&!this.isReservedWord(t)){r.locals[t]=i}else{r[t]=i}}}}}findNext(e,t){if(e){if(e.resolveNext){return e.resolveNext(t)}else if(e.next){return e.next}else{return this.findNext(e.parent,t)}}}flatGet(e,t,r){if(e!=null){var n=r(e,t);if(typeof n!=="undefined"){return n}if(this.shouldAutoIterate(e)){var i=[];for(var a of e){var o=r(a,t);i.push(o)}return i}}}resolveProperty(e,t){return this.flatGet(e,t,((e,t)=>e[t]))}resolveAttribute(e,t){return this.flatGet(e,t,((e,t)=>e.getAttribute&&e.getAttribute(t)))}resolveStyle(e,t){return this.flatGet(e,t,((e,t)=>e.style&&e.style[t]))}resolveComputedStyle(e,t){return this.flatGet(e,t,((e,t)=>getComputedStyle(e).getPropertyValue(t)))}assignToNamespace(t,r,n,i){let a;if(typeof document!=="undefined"&&t===document.body){a=e}else{a=this.getHyperscriptFeatures(t)}var o;while((o=r.shift())!==undefined){var s=a[o];if(s==null){s={};a[o]=s}a=s}a[n]=i}getHyperTrace(e,t){var r=[];var n=e;while(n.meta.caller){n=n.meta.caller}if(n.meta.traceMap){return n.meta.traceMap.get(t,r)}}registerHyperTrace(e,t){var r=[];var n=null;while(e!=null){r.push(e);n=e;e=e.meta.caller}if(n.meta.traceMap==null){n.meta.traceMap=new Map}if(!n.meta.traceMap.get(t)){var i={trace:r,print:function(e){e=e||console.error;e("hypertrace /// ");var t=0;for(var n=0;n",i.meta.feature.displayName.padEnd(t+2),"-",i.meta.owner)}}};n.meta.traceMap.set(t,i)}}escapeSelector(e){return e.replace(/:/g,(function(e){return"\\"+e}))}nullCheck(e,t){if(e==null){throw new Error("'"+t.sourceFor()+"' is null")}}isEmpty(e){return e==undefined||e.length===0}doesExist(e){if(e==null){return false}if(this.shouldAutoIterate(e)){for(const t of e){return true}return false}return true}getRootNode(e){if(e&&e instanceof Node){var t=e.getRootNode();if(t instanceof Document||t instanceof ShadowRoot)return t}return document}getEventQueueFor(e,t){let r=this.getInternalData(e);var n=r.eventQueues;if(n==null){n=new Map;r.eventQueues=n}var i=n.get(t);if(i==null){i={queue:[],executing:false};n.set(t,i)}return i}beepValueToConsole(e,t,r){if(this.triggerEvent(e,"hyperscript:beep",{element:e,expression:t,value:r})){var n;if(r){if(r instanceof m){n="ElementCollection"}else if(r.constructor){n=r.constructor.name}else{n="unknown"}}else{n="object (null)"}var a=r;if(n==="String"){a='"'+a+'"'}else if(r instanceof m){a=Array.from(r)}console.log("///_ BEEP! The expression ("+i.sourceFor.call(t).replace("beep! ","")+") evaluates to:",a,"of type "+n)}}hyperscriptUrl="document"in e&&document.currentScript?document.currentScript.src:null}function s(){let e=document.cookie.split("; ").map((e=>{let t=e.split("=");return{name:t[0],value:decodeURIComponent(t[1])}}));return e}function u(e){document.cookie=e+"=;expires=Thu, 01 Jan 1970 00:00:00 GMT"}function l(){for(const e of s()){u(e.name)}}const c=new Proxy({},{get(e,t){if(t==="then"||t==="asyncWrapper"){return null}else if(t==="length"){return s().length}else if(t==="clear"){return u}else if(t==="clearAll"){return l}else if(typeof t==="string"){if(!isNaN(t)){return s()[parseInt(t)]}else{let e=document.cookie.split("; ").find((e=>e.startsWith(t+"=")))?.split("=")[1];if(e){return decodeURIComponent(e)}}}else if(t===Symbol.iterator){return s()[t]}},set(e,t,r){var n=null;if("string"===typeof r){n=encodeURIComponent(r);n+=";samesite=lax"}else{n=encodeURIComponent(r.value);if(r.expires){n+=";expires="+r.maxAge}if(r.maxAge){n+=";max-age="+r.maxAge}if(r.partitioned){n+=";partitioned="+r.partitioned}if(r.path){n+=";path="+r.path}if(r.samesite){n+=";samesite="+r.path}if(r.secure){n+=";secure="+r.path}}document.cookie=t+"="+n;return true}});class f{constructor(t,r,n,i,a){this.meta={parser:a.parser,lexer:a.lexer,runtime:a,owner:t,feature:r,iterators:{},ctx:this};this.locals={cookies:c};this.me=n,this.you=undefined;this.result=undefined;this.event=i;this.target=i?i.target:null;this.detail=i?i.detail:null;this.sender=i?i.detail?i.detail.sender:null:null;this.body="document"in e?document.body:null;a.addFeatures(t,this)}}class m{constructor(e,t,r){this._css=e;this.relativeToElement=t;this.escape=r;this[p]=true}get css(){if(this.escape){return o.prototype.escapeSelector(this._css)}else{return this._css}}get className(){return this._css.substr(1)}get id(){return this.className()}contains(e){for(let t of this){if(t.contains(e)){return true}}return false}get length(){return this.selectMatches().length}[Symbol.iterator](){let e=this.selectMatches();return e[Symbol.iterator]()}selectMatches(){let e=o.prototype.getRootNode(this.relativeToElement).querySelectorAll(this.css);return e}}const p=Symbol();function h(e,t){var r=e[t];if(r){return r}else{var n={};e[t]=n;return n}}function v(e){try{return JSON.parse(e)}catch(e){d(e);return null}}function d(e){if(console.error){console.error(e)}else if(console.log){console.log("ERROR: ",e)}}function E(e,t){return new(e.bind.apply(e,[e].concat(t)))}function T(t){t.addLeafExpression("parenthesized",(function(e,t,r){if(r.matchOpToken("(")){var n=r.clearFollows();try{var i=e.requireElement("expression",r)}finally{r.restoreFollows(n)}r.requireOpToken(")");return i}}));t.addLeafExpression("string",(function(e,t,r){var i=r.matchTokenType("STRING");if(!i)return;var a=i.value;var o;if(i.template){var s=n.tokenize(a,true);o=e.parseStringTemplate(s)}else{o=[]}return{type:"string",token:i,args:o,op:function(e){var t="";for(var r=1;re instanceof Element))}get css(){let e="",t=0;for(const r of this.templateParts){if(r instanceof Element){e+="[data-hs-query-id='"+t+++"']"}else e+=r}return e}[Symbol.iterator](){this.elements.forEach(((e,t)=>e.dataset.hsQueryId=t));const e=super[Symbol.iterator]();this.elements.forEach((e=>e.removeAttribute("data-hs-query-id")));return e}}t.addLeafExpression("queryRef",(function(e,t,i){var a=i.matchOpToken("<");if(!a)return;var o=i.consumeUntil("/");i.requireOpToken("/");i.requireOpToken(">");var s=o.map((function(e){if(e.type==="STRING"){return'"'+e.value+'"'}else{return e.value}})).join("");var u,l,c;if(s.indexOf("$")>=0){u=true;l=n.tokenize(s,true);c=e.parseStringTemplate(l)}return{type:"queryRef",css:s,args:c,op:function(e,...t){if(u){return new r(s,e.me,t)}else{return new m(s,e.me)}},evaluate:function(e){return t.unifiedEval(this,e)}}}));t.addLeafExpression("attributeRef",(function(e,t,r){var n=r.matchTokenType("ATTRIBUTE_REF");if(!n)return;if(!n.value)return;var i=n.value;if(i.indexOf("[")===0){var a=i.substring(2,i.length-1)}else{var a=i.substring(1)}var o="["+a+"]";var s=a.split("=");var u=s[0];var l=s[1];if(l){if(l.indexOf('"')===0){l=l.substring(1,l.length-1)}}return{type:"attributeRef",name:u,css:o,value:l,op:function(e){var t=e.you||e.me;if(t){return t.getAttribute(u)}},evaluate:function(e){return t.unifiedEval(this,e)}}}));t.addLeafExpression("styleRef",(function(e,t,r){var n=r.matchTokenType("STYLE_REF");if(!n)return;if(!n.value)return;var i=n.value.substr(1);if(i.startsWith("computed-")){i=i.substr("computed-".length);return{type:"computedStyleRef",name:i,op:function(e){var r=e.you||e.me;if(r){return t.resolveComputedStyle(r,i)}},evaluate:function(e){return t.unifiedEval(this,e)}}}else{return{type:"styleRef",name:i,op:function(e){var r=e.you||e.me;if(r){return t.resolveStyle(r,i)}},evaluate:function(e){return t.unifiedEval(this,e)}}}}));t.addGrammarElement("objectKey",(function(e,t,r){var n;if(n=r.matchTokenType("STRING")){return{type:"objectKey",key:n.value,evaluate:function(){return n.value}}}else if(r.matchOpToken("[")){var i=e.parseElement("expression",r);r.requireOpToken("]");return{type:"objectKey",expr:i,args:[i],op:function(e,t){return t},evaluate:function(e){return t.unifiedEval(this,e)}}}else{var a="";do{n=r.matchTokenType("IDENTIFIER")||r.matchOpToken("-");if(n)a+=n.value}while(n);return{type:"objectKey",key:a,evaluate:function(){return a}}}}));t.addLeafExpression("objectLiteral",(function(e,t,r){if(!r.matchOpToken("{"))return;var n=[];var i=[];if(!r.matchOpToken("}")){do{var a=e.requireElement("objectKey",r);r.requireOpToken(":");var o=e.requireElement("expression",r);i.push(o);n.push(a)}while(r.matchOpToken(","));r.requireOpToken("}")}return{type:"objectLiteral",args:[n,i],op:function(e,t,r){var n={};for(var i=0;i");var a=e.requireElement("expression",r);return{type:"blockLiteral",args:n,expr:a,evaluate:function(e){var t=function(){for(var t=0;t=0;a--){var o=i[a];if(o.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return o}}if(n){return i[i.length-1]}};var l=function(e,t,r,n){var i=[];o.prototype.forEach(t,(function(t){if(t.matches(r)||t===e){i.push(t)}}));for(var a=0;a","<=",">=","==","===","!=","!==");var a=i?i.value:null;var o=true;var s=false;if(a==null){if(r.matchToken("is")||r.matchToken("am")){if(r.matchToken("not")){if(r.matchToken("in")){a="not in"}else if(r.matchToken("a")){a="not a";s=true}else if(r.matchToken("empty")){a="not empty";o=false}else{if(r.matchToken("really")){a="!=="}else{a="!="}if(r.matchToken("equal")){r.matchToken("to")}}}else if(r.matchToken("in")){a="in"}else if(r.matchToken("a")){a="a";s=true}else if(r.matchToken("empty")){a="empty";o=false}else if(r.matchToken("less")){r.requireToken("than");if(r.matchToken("or")){r.requireToken("equal");r.requireToken("to");a="<="}else{a="<"}}else if(r.matchToken("greater")){r.requireToken("than");if(r.matchToken("or")){r.requireToken("equal");r.requireToken("to");a=">="}else{a=">"}}else{if(r.matchToken("really")){a="==="}else{a="=="}if(r.matchToken("equal")){r.matchToken("to")}}}else if(r.matchToken("equals")){a="=="}else if(r.matchToken("really")){r.requireToken("equals");a="==="}else if(r.matchToken("exist")||r.matchToken("exists")){a="exist";o=false}else if(r.matchToken("matches")||r.matchToken("match")){a="match"}else if(r.matchToken("contains")||r.matchToken("contain")){a="contain"}else if(r.matchToken("includes")||r.matchToken("include")){a="include"}else if(r.matchToken("do")||r.matchToken("does")){r.requireToken("not");if(r.matchToken("matches")||r.matchToken("match")){a="not match"}else if(r.matchToken("contains")||r.matchToken("contain")){a="not contain"}else if(r.matchToken("exist")||r.matchToken("exist")){a="not exist";o=false}else if(r.matchToken("include")){a="not include"}else{e.raiseParseError(r,"Expected matches or contains")}}}if(a){var u,l,c;if(s){u=r.requireTokenType("IDENTIFIER");l=!r.matchOpToken("!")}else if(o){c=e.requireElement("mathExpression",r);if(a==="match"||a==="not match"){c=c.css?c.css:c}}var m=n;n={type:"comparisonOperator",operator:a,typeName:u,nullOk:l,lhs:n,rhs:c,args:[n,c],op:function(e,r,n){if(a==="=="){return r==n}else if(a==="!="){return r!=n}if(a==="==="){return r===n}else if(a==="!=="){return r!==n}if(a==="match"){return r!=null&&p(m,r,n)}if(a==="not match"){return r==null||!p(m,r,n)}if(a==="in"){return n!=null&&f(c,n,r)}if(a==="not in"){return n==null||!f(c,n,r)}if(a==="contain"){return r!=null&&f(m,r,n)}if(a==="not contain"){return r==null||!f(m,r,n)}if(a==="include"){return r!=null&&f(m,r,n)}if(a==="not include"){return r==null||!f(m,r,n)}if(a==="==="){return r===n}else if(a==="!=="){return r!==n}else if(a==="<"){return r"){return r>n}else if(a==="<="){return r<=n}else if(a===">="){return r>=n}else if(a==="empty"){return t.isEmpty(r)}else if(a==="not empty"){return!t.isEmpty(r)}else if(a==="exist"){return t.doesExist(r)}else if(a==="not exist"){return!t.doesExist(r)}else if(a==="a"){return t.typeCheck(r,u.value,l)}else if(a==="not a"){return!t.typeCheck(r,u.value,l)}else{throw"Unknown comparison : "+a}},evaluate:function(e){return t.unifiedEval(this,e)}}}return n}));t.addGrammarElement("comparisonExpression",(function(e,t,r){return e.parseAnyOf(["comparisonOperator","mathExpression"],r)}));t.addGrammarElement("logicalOperator",(function(e,t,r){var n=e.parseElement("comparisonExpression",r);var i,a=null;i=r.matchToken("and")||r.matchToken("or");while(i){a=a||i;if(a.value!==i.value){e.raiseParseError(r,"You must parenthesize logical operations with different operators")}var o=e.requireElement("comparisonExpression",r);const s=i.value;n={type:"logicalOperator",operator:s,lhs:n,rhs:o,args:[n,o],op:function(e,t,r){if(s==="and"){return t&&r}else{return t||r}},evaluate:function(e){return t.unifiedEval(this,e)}};i=r.matchToken("and")||r.matchToken("or")}return n}));t.addGrammarElement("logicalExpression",(function(e,t,r){return e.parseAnyOf(["logicalOperator","mathExpression"],r)}));t.addGrammarElement("asyncExpression",(function(e,t,r){if(r.matchToken("async")){var n=e.requireElement("logicalExpression",r);var i={type:"asyncExpression",value:n,evaluate:function(e){return{asyncWrapper:true,value:this.value.evaluate(e)}}};return i}else{return e.parseElement("logicalExpression",r)}}));t.addGrammarElement("expression",(function(e,t,r){r.matchToken("the");return e.parseElement("asyncExpression",r)}));t.addGrammarElement("assignableExpression",(function(e,t,r){r.matchToken("the");var n=e.parseElement("primaryExpression",r);if(n&&(n.type==="symbol"||n.type==="ofExpression"||n.type==="propertyAccess"||n.type==="attributeRefAccess"||n.type==="attributeRef"||n.type==="styleRef"||n.type==="arrayIndex"||n.type==="possessive")){return n}else{e.raiseParseError(r,"A target expression must be writable. The expression type '"+(n&&n.type)+"' is not.")}return n}));t.addGrammarElement("hyperscript",(function(e,t,r){var n=[];if(r.hasMore()){while(e.featureStart(r.currentToken())||r.currentToken().value==="("){var i=e.requireElement("feature",r);n.push(i);r.matchToken("end")}}return{type:"hyperscript",features:n,apply:function(e,t,r){for(const i of n){i.install(e,t,r)}}}}));var v=function(e){var t=[];if(e.token(0).value==="("&&(e.token(1).value===")"||e.token(2).value===","||e.token(2).value===")")){e.matchOpToken("(");do{t.push(e.requireTokenType("IDENTIFIER"))}while(e.matchOpToken(","));e.requireOpToken(")")}return t};t.addFeature("on",(function(e,t,r){if(!r.matchToken("on"))return;var n=false;if(r.matchToken("every")){n=true}var i=[];var a=null;do{var o=e.requireElement("eventName",r,"Expected event name");var s=o.evaluate();if(a){a=a+" or "+s}else{a="on "+s}var u=v(r);var l=null;if(r.matchOpToken("[")){l=e.requireElement("expression",r);r.requireOpToken("]")}var c,f,m;if(r.currentToken().type==="NUMBER"){var p=r.consumeToken();if(!p.value)return;c=parseInt(p.value);if(r.matchToken("to")){var h=r.consumeToken();if(!h.value)return;f=parseInt(h.value)}else if(r.matchToken("and")){m=true;r.requireToken("on")}}var d,E;if(s==="intersection"){d={};if(r.matchToken("with")){d["with"]=e.requireElement("expression",r).evaluate()}if(r.matchToken("having")){do{if(r.matchToken("margin")){d["rootMargin"]=e.requireElement("stringLike",r).evaluate()}else if(r.matchToken("threshold")){d["threshold"]=e.requireElement("expression",r).evaluate()}else{e.raiseParseError(r,"Unknown intersection config specification")}}while(r.matchToken("and"))}}else if(s==="mutation"){E={};if(r.matchToken("of")){do{if(r.matchToken("anything")){E["attributes"]=true;E["subtree"]=true;E["characterData"]=true;E["childList"]=true}else if(r.matchToken("childList")){E["childList"]=true}else if(r.matchToken("attributes")){E["attributes"]=true;E["attributeOldValue"]=true}else if(r.matchToken("subtree")){E["subtree"]=true}else if(r.matchToken("characterData")){E["characterData"]=true;E["characterDataOldValue"]=true}else if(r.currentToken().type==="ATTRIBUTE_REF"){var T=r.consumeToken();if(E["attributeFilter"]==null){E["attributeFilter"]=[]}if(T.value.indexOf("@")==0){E["attributeFilter"].push(T.value.substring(1))}else{e.raiseParseError(r,"Only shorthand attribute references are allowed here")}}else{e.raiseParseError(r,"Unknown mutation config specification")}}while(r.matchToken("or"))}else{E["attributes"]=true;E["characterData"]=true;E["childList"]=true}}var y=null;var k=false;if(r.matchToken("from")){if(r.matchToken("elsewhere")){k=true}else{r.pushFollow("or");try{y=e.requireElement("expression",r)}finally{r.popFollow()}if(!y){e.raiseParseError(r,'Expected either target value or "elsewhere".')}}}if(y===null&&k===false&&r.matchToken("elsewhere")){k=true}if(r.matchToken("in")){var x=e.parseElement("unaryExpression",r)}if(r.matchToken("debounced")){r.requireToken("at");var g=e.requireElement("unaryExpression",r);var b=g.evaluate({})}else if(r.matchToken("throttled")){r.requireToken("at");var g=e.requireElement("unaryExpression",r);var w=g.evaluate({})}i.push({execCount:0,every:n,on:s,args:u,filter:l,from:y,inExpr:x,elsewhere:k,startCount:c,endCount:f,unbounded:m,debounceTime:b,throttleTime:w,mutationSpec:E,intersectionSpec:d,debounced:undefined,lastExec:undefined})}while(r.matchToken("or"));var S=true;if(!n){if(r.matchToken("queue")){if(r.matchToken("all")){var q=true;var S=false}else if(r.matchToken("first")){var N=true}else if(r.matchToken("none")){var I=true}else{r.requireToken("last")}}}var C=e.requireElement("commandList",r);e.ensureTerminated(C);var R,A;if(r.matchToken("catch")){R=r.requireTokenType("IDENTIFIER").value;A=e.requireElement("commandList",r);e.ensureTerminated(A)}if(r.matchToken("finally")){var L=e.requireElement("commandList",r);e.ensureTerminated(L)}var O={displayName:a,events:i,start:C,every:n,execCount:0,errorHandler:A,errorSymbol:R,execute:function(e){let r=t.getEventQueueFor(e.me,O);if(r.executing&&n===false){if(I||N&&r.queue.length>0){return}if(S){r.queue.length=0}r.queue.push(e);return}O.execCount++;r.executing=true;e.meta.onHalt=function(){r.executing=false;var e=r.queue.shift();if(e){setTimeout((function(){O.execute(e)}),1)}};e.meta.reject=function(r){console.error(r.message?r.message:r);var n=t.getHyperTrace(e,r);if(n){n.print()}t.triggerEvent(e.me,"exception",{error:r})};C.execute(e)},install:function(e,r){for(const r of O.events){var n;if(r.elsewhere){n=[document]}else if(r.from){n=r.from.evaluate(t.makeContext(e,O,e,null))}else{n=[e]}t.implicitLoop(n,(function(n){var i=r.on;if(n==null){console.warn("'%s' feature ignored because target does not exists:",a,e);return}if(r.mutationSpec){i="hyperscript:mutation";const e=new MutationObserver((function(e,r){if(!O.executing){t.triggerEvent(n,i,{mutationList:e,observer:r})}}));e.observe(n,r.mutationSpec)}if(r.intersectionSpec){i="hyperscript:intersection";const e=new IntersectionObserver((function(r){for(const o of r){var a={observer:e};a=Object.assign(a,o);a["intersecting"]=o.isIntersecting;t.triggerEvent(n,i,a)}}),r.intersectionSpec);e.observe(n)}var o=n.addEventListener||n.on;o.call(n,i,(function a(o){if(typeof Node!=="undefined"&&e instanceof Node&&n!==e&&!e.isConnected){n.removeEventListener(i,a);return}var s=t.makeContext(e,O,e,o);if(r.elsewhere&&e.contains(o.target)){return}if(r.from){s.result=n}for(const e of r.args){let t=s.event[e.value];if(t!==undefined){s.locals[e.value]=t}else if("detail"in s.event){s.locals[e.value]=s.event["detail"][e.value]}}s.meta.errorHandler=A;s.meta.errorSymbol=R;s.meta.finallyHandler=L;if(r.filter){var u=s.meta.context;s.meta.context=s.event;try{var l=r.filter.evaluate(s);if(l){}else{return}}finally{s.meta.context=u}}if(r.inExpr){var c=o.target;while(true){if(c.matches&&c.matches(r.inExpr.css)){s.result=c;break}else{c=c.parentElement;if(c==null){return}}}}r.execCount++;if(r.startCount){if(r.endCount){if(r.execCountr.endCount){return}}else if(r.unbounded){if(r.execCount{var a=false;for(const s of i){var o=n=>{e.result=n;if(s.args){for(const t of s.args){e.locals[t.value]=n[t.value]||(n.detail?n.detail[t.value]:null)}}if(!a){a=true;r(t.findNext(this,e))}};if(s.name){n.addEventListener(s.name,o,{once:true})}else if(s.time!=null){setTimeout(o,s.time,s.time)}}}))}};return n}else{var s;if(r.matchToken("a")){r.requireToken("tick");s=0}else{s=e.requireElement("expression",r)}n={type:"waitCmd",time:s,args:[s],op:function(e,r){return new Promise((n=>{setTimeout((()=>{n(t.findNext(this,e))}),r)}))},execute:function(e){return t.unifiedExec(this,e)}};return n}}));t.addGrammarElement("dotOrColonPath",(function(e,t,r){var n=r.matchTokenType("IDENTIFIER");if(n){var i=[n.value];var a=r.matchOpToken(".")||r.matchOpToken(":");if(a){do{i.push(r.requireTokenType("IDENTIFIER","NUMBER").value)}while(r.matchOpToken(a.value))}return{type:"dotOrColonPath",path:i,evaluate:function(){return i.join(a?a.value:"")}}}}));t.addGrammarElement("eventName",(function(e,t,r){var n;if(n=r.matchTokenType("STRING")){return{evaluate:function(){return n.value}}}return e.parseElement("dotOrColonPath",r)}));function d(e,t,r,n){var i=t.requireElement("eventName",n);var a=t.parseElement("namedArgumentList",n);if(e==="send"&&n.matchToken("to")||e==="trigger"&&n.matchToken("on")){var o=t.requireElement("expression",n)}else{var o=t.requireElement("implicitMeTarget",n)}var s={eventName:i,details:a,to:o,args:[o,i,a],op:function(e,t,n,i){r.nullCheck(t,o);r.implicitLoop(t,(function(t){r.triggerEvent(t,n,i,e.me)}));return r.findNext(s,e)}};return s}t.addCommand("trigger",(function(e,t,r){if(r.matchToken("trigger")){return d("trigger",e,t,r)}}));t.addCommand("send",(function(e,t,r){if(r.matchToken("send")){return d("send",e,t,r)}}));var T=function(e,t,r,n){if(n){if(e.commandBoundary(r.currentToken())){e.raiseParseError(r,"'return' commands must return a value. If you do not wish to return a value, use 'exit' instead.")}else{var i=e.requireElement("expression",r)}}var a={value:i,args:[i],op:function(e,r){var n=e.meta.resolve;e.meta.returned=true;e.meta.returnValue=r;if(n){if(r){n(r)}else{n()}}return t.HALT}};return a};t.addCommand("return",(function(e,t,r){if(r.matchToken("return")){return T(e,t,r,true)}}));t.addCommand("exit",(function(e,t,r){if(r.matchToken("exit")){return T(e,t,r,false)}}));t.addCommand("halt",(function(e,t,r){if(r.matchToken("halt")){if(r.matchToken("the")){r.requireToken("event");if(r.matchOpToken("'")){r.requireToken("s")}var n=true}if(r.matchToken("bubbling")){var i=true}else if(r.matchToken("default")){var a=true}var o=T(e,t,r,false);var s={keepExecuting:true,bubbling:i,haltDefault:a,exit:o,op:function(e){if(e.event){if(i){e.event.stopPropagation()}else if(a){e.event.preventDefault()}else{e.event.stopPropagation();e.event.preventDefault()}if(n){return t.findNext(this,e)}else{return o}}}};return s}}));t.addCommand("log",(function(e,t,r){if(!r.matchToken("log"))return;var n=[e.parseElement("expression",r)];while(r.matchOpToken(",")){n.push(e.requireElement("expression",r))}if(r.matchToken("with")){var i=e.requireElement("expression",r)}var a={exprs:n,withExpr:i,args:[i,n],op:function(e,r,n){if(r){r.apply(null,n)}else{console.log.apply(null,n)}return t.findNext(this,e)}};return a}));t.addCommand("beep!",(function(e,t,r){if(!r.matchToken("beep!"))return;var n=[e.parseElement("expression",r)];while(r.matchOpToken(",")){n.push(e.requireElement("expression",r))}var i={exprs:n,args:[n],op:function(e,r){for(let i=0;i{if(!r.matchToken("pick"))return;r.matchToken("the");if(r.matchToken("item")||r.matchToken("items")||r.matchToken("character")||r.matchToken("characters")){const n=g(e,t,r);r.requireToken("from");const i=e.requireElement("expression",r);return{args:[i,n.from,n.to],op(e,r,i,a){if(n.toEnd)a=r.length;if(!n.includeStart)i++;if(n.includeEnd)a++;if(a==null||a==undefined)a=i+1;e.result=r.slice(i,a);return t.findNext(this,e)}}}if(r.matchToken("match")){r.matchToken("of");const n=e.parseElement("expression",r);let i="";if(r.matchOpToken("|")){i=r.requireToken("identifier").value}r.requireToken("from");const a=e.parseElement("expression",r);return{args:[a,n],op(e,r,n){e.result=new RegExp(n,i).exec(r);return t.findNext(this,e)}}}if(r.matchToken("matches")){r.matchToken("of");const n=e.parseElement("expression",r);let i="gu";if(r.matchOpToken("|")){i="g"+r.requireToken("identifier").value.replace("g","")}console.log("flags",i);r.requireToken("from");const a=e.parseElement("expression",r);return{args:[a,n],op(e,r,n){e.result=new w(n,i,r);return t.findNext(this,e)}}}}));t.addCommand("increment",(function(e,t,r){if(!r.matchToken("increment"))return;var n;var i=e.parseElement("assignableExpression",r);if(r.matchToken("by")){n=e.requireElement("expression",r)}var a={type:"implicitIncrementOp",target:i,args:[i,n],op:function(e,t,r){t=t?parseFloat(t):0;r=n?parseFloat(r):1;var i=t+r;e.result=i;return i},evaluate:function(e){return t.unifiedEval(this,e)}};return k(e,t,r,i,a)}));t.addCommand("decrement",(function(e,t,r){if(!r.matchToken("decrement"))return;var n;var i=e.parseElement("assignableExpression",r);if(r.matchToken("by")){n=e.requireElement("expression",r)}var a={type:"implicitDecrementOp",target:i,args:[i,n],op:function(e,t,r){t=t?parseFloat(t):0;r=n?parseFloat(r):1;var i=t-r;e.result=i;return i},evaluate:function(e){return t.unifiedEval(this,e)}};return k(e,t,r,i,a)}));function S(e,t){var r="text";var n;e.matchToken("a")||e.matchToken("an");if(e.matchToken("json")||e.matchToken("Object")){r="json"}else if(e.matchToken("response")){r="response"}else if(e.matchToken("html")){r="html"}else if(e.matchToken("text")){}else{n=t.requireElement("dotOrColonPath",e).evaluate()}return{type:r,conversion:n}}t.addCommand("fetch",(function(e,t,r){if(!r.matchToken("fetch"))return;var n=e.requireElement("stringLike",r);if(r.matchToken("as")){var i=S(r,e)}if(r.matchToken("with")&&r.currentToken().value!=="{"){var a=e.parseElement("nakedNamedArgumentList",r)}else{var a=e.parseElement("objectLiteral",r)}if(i==null&&r.matchToken("as")){i=S(r,e)}var o=i?i.type:"text";var s=i?i.conversion:null;var u={url:n,argExpressions:a,args:[n,a],op:function(e,r,n){var i=n||{};i["sender"]=e.me;i["headers"]=i["headers"]||{};var a=new AbortController;let l=e.me.addEventListener("fetch:abort",(function(){a.abort()}),{once:true});i["signal"]=a.signal;t.triggerEvent(e.me,"hyperscript:beforeFetch",i);t.triggerEvent(e.me,"fetch:beforeRequest",i);n=i;var c=false;if(n.timeout){setTimeout((function(){if(!c){a.abort()}}),n.timeout)}return fetch(r,n).then((function(r){let n={response:r};t.triggerEvent(e.me,"fetch:afterResponse",n);r=n.response;if(o==="response"){e.result=r;t.triggerEvent(e.me,"fetch:afterRequest",{result:r});c=true;return t.findNext(u,e)}if(o==="json"){return r.json().then((function(r){e.result=r;t.triggerEvent(e.me,"fetch:afterRequest",{result:r});c=true;return t.findNext(u,e)}))}return r.text().then((function(r){if(s)r=t.convertValue(r,s);if(o==="html")r=t.convertValue(r,"Fragment");e.result=r;t.triggerEvent(e.me,"fetch:afterRequest",{result:r});c=true;return t.findNext(u,e)}))})).catch((function(r){t.triggerEvent(e.me,"fetch:error",{reason:r});throw r})).finally((function(){e.me.removeEventListener("fetch:abort",l)}))}};return u}))}function y(e){e.addCommand("settle",(function(e,t,r){if(r.matchToken("settle")){if(!e.commandBoundary(r.currentToken())){var n=e.requireElement("expression",r)}else{var n=e.requireElement("implicitMeTarget",r)}var i={type:"settleCmd",args:[n],op:function(e,r){t.nullCheck(r,n);var a=null;var o=false;var s=false;var u=new Promise((function(e){a=e}));r.addEventListener("transitionstart",(function(){s=true}),{once:true});setTimeout((function(){if(!s&&!o){a(t.findNext(i,e))}}),500);r.addEventListener("transitionend",(function(){if(!o){a(t.findNext(i,e))}}),{once:true});return u},execute:function(e){return t.unifiedExec(this,e)}};return i}}));e.addCommand("add",(function(e,t,r){if(r.matchToken("add")){var n=e.parseElement("classRef",r);var i=null;var a=null;if(n==null){i=e.parseElement("attributeRef",r);if(i==null){a=e.parseElement("styleLiteral",r);if(a==null){e.raiseParseError(r,"Expected either a class reference or attribute expression")}}}else{var o=[n];while(n=e.parseElement("classRef",r)){o.push(n)}}if(r.matchToken("to")){var s=e.requireElement("expression",r)}else{var s=e.requireElement("implicitMeTarget",r)}if(r.matchToken("when")){if(a){e.raiseParseError(r,"Only class and properties are supported with a when clause")}var u=e.requireElement("expression",r)}if(o){return{classRefs:o,to:s,args:[s,o],op:function(e,r,n){t.nullCheck(r,s);t.forEach(n,(function(n){t.implicitLoop(r,(function(r){if(u){e.result=r;let i=t.evaluateNoPromise(u,e);if(i){if(r instanceof Element)r.classList.add(n.className)}else{if(r instanceof Element)r.classList.remove(n.className)}e.result=null}else{if(r instanceof Element)r.classList.add(n.className)}}))}));return t.findNext(this,e)}}}else if(i){return{type:"addCmd",attributeRef:i,to:s,args:[s],op:function(e,r,n){t.nullCheck(r,s);t.implicitLoop(r,(function(r){if(u){e.result=r;let n=t.evaluateNoPromise(u,e);if(n){r.setAttribute(i.name,i.value)}else{r.removeAttribute(i.name)}e.result=null}else{r.setAttribute(i.name,i.value)}}));return t.findNext(this,e)},execute:function(e){return t.unifiedExec(this,e)}}}else{return{type:"addCmd",cssDeclaration:a,to:s,args:[s,a],op:function(e,r,n){t.nullCheck(r,s);t.implicitLoop(r,(function(e){e.style.cssText+=n}));return t.findNext(this,e)},execute:function(e){return t.unifiedExec(this,e)}}}}}));e.addGrammarElement("styleLiteral",(function(e,t,r){if(!r.matchOpToken("{"))return;var n=[""];var i=[];while(r.hasMore()){if(r.matchOpToken("\\")){r.consumeToken()}else if(r.matchOpToken("}")){break}else if(r.matchToken("$")){var a=r.matchOpToken("{");var o=e.parseElement("expression",r);if(a)r.requireOpToken("}");i.push(o);n.push("")}else{var s=r.consumeToken();n[n.length-1]+=r.source.substring(s.start,s.end)}n[n.length-1]+=r.lastWhitespace()}return{type:"styleLiteral",args:[i],op:function(e,t){var r="";n.forEach((function(e,n){r+=e;if(n in t)r+=t[n]}));return r},evaluate:function(e){return t.unifiedEval(this,e)}}}));e.addCommand("remove",(function(e,t,r){if(r.matchToken("remove")){var n=e.parseElement("classRef",r);var i=null;var a=null;if(n==null){i=e.parseElement("attributeRef",r);if(i==null){a=e.parseElement("expression",r);if(a==null){e.raiseParseError(r,"Expected either a class reference, attribute expression or value expression")}}}else{var o=[n];while(n=e.parseElement("classRef",r)){o.push(n)}}if(r.matchToken("from")){var s=e.requireElement("expression",r)}else{if(a==null){var s=e.requireElement("implicitMeTarget",r)}}if(a){return{elementExpr:a,from:s,args:[a,s],op:function(e,r,n){t.nullCheck(r,a);t.implicitLoop(r,(function(e){if(e.parentElement&&(n==null||n.contains(e))){e.parentElement.removeChild(e)}}));return t.findNext(this,e)}}}else{return{classRefs:o,attributeRef:i,elementExpr:a,from:s,args:[o,s],op:function(e,r,n){t.nullCheck(n,s);if(r){t.forEach(r,(function(e){t.implicitLoop(n,(function(t){t.classList.remove(e.className)}))}))}else{t.implicitLoop(n,(function(e){e.removeAttribute(i.name)}))}return t.findNext(this,e)}}}}}));e.addCommand("toggle",(function(e,t,r){if(r.matchToken("toggle")){r.matchAnyToken("the","my");if(r.currentToken().type==="STYLE_REF"){let t=r.consumeToken();var n=t.value.substr(1);var a=true;var o=i(e,r,n);if(r.matchToken("of")){r.pushFollow("with");try{var s=e.requireElement("expression",r)}finally{r.popFollow()}}else{var s=e.requireElement("implicitMeTarget",r)}}else if(r.matchToken("between")){var u=true;var l=e.parseElement("classRef",r);r.requireToken("and");var c=e.requireElement("classRef",r)}else{var l=e.parseElement("classRef",r);var f=null;if(l==null){f=e.parseElement("attributeRef",r);if(f==null){e.raiseParseError(r,"Expected either a class reference or attribute expression")}}else{var m=[l];while(l=e.parseElement("classRef",r)){m.push(l)}}}if(a!==true){if(r.matchToken("on")){var s=e.requireElement("expression",r)}else{var s=e.requireElement("implicitMeTarget",r)}}if(r.matchToken("for")){var p=e.requireElement("expression",r)}else if(r.matchToken("until")){var h=e.requireElement("dotOrColonPath",r,"Expected event name");if(r.matchToken("from")){var v=e.requireElement("expression",r)}}var d={classRef:l,classRef2:c,classRefs:m,attributeRef:f,on:s,time:p,evt:h,from:v,toggle:function(e,r,n,i){t.nullCheck(e,s);if(a){t.implicitLoop(e,(function(e){o("toggle",e)}))}else if(u){t.implicitLoop(e,(function(e){if(e.classList.contains(r.className)){e.classList.remove(r.className);e.classList.add(n.className)}else{e.classList.add(r.className);e.classList.remove(n.className)}}))}else if(i){t.forEach(i,(function(r){t.implicitLoop(e,(function(e){e.classList.toggle(r.className)}))}))}else{t.forEach(e,(function(e){if(e.hasAttribute(f.name)){e.removeAttribute(f.name)}else{e.setAttribute(f.name,f.value)}}))}},args:[s,p,h,v,l,c,m],op:function(e,r,n,i,a,o,s,u){if(n){return new Promise((function(i){d.toggle(r,o,s,u);setTimeout((function(){d.toggle(r,o,s,u);i(t.findNext(d,e))}),n)}))}else if(i){return new Promise((function(n){var l=a||e.me;l.addEventListener(i,(function(){d.toggle(r,o,s,u);n(t.findNext(d,e))}),{once:true});d.toggle(r,o,s,u)}))}else{this.toggle(r,o,s,u);return t.findNext(d,e)}}};return d}}));var t={display:function(r,n,i){if(i){n.style.display=i}else if(r==="toggle"){if(getComputedStyle(n).display==="none"){t.display("show",n,i)}else{t.display("hide",n,i)}}else if(r==="hide"){const t=e.runtime.getInternalData(n);if(t.originalDisplay==null){t.originalDisplay=n.style.display}n.style.display="none"}else{const t=e.runtime.getInternalData(n);if(t.originalDisplay&&t.originalDisplay!=="none"){n.style.display=t.originalDisplay}else{n.style.removeProperty("display")}}},visibility:function(e,r,n){if(n){r.style.visibility=n}else if(e==="toggle"){if(getComputedStyle(r).visibility==="hidden"){t.visibility("show",r,n)}else{t.visibility("hide",r,n)}}else if(e==="hide"){r.style.visibility="hidden"}else{r.style.visibility="visible"}},opacity:function(e,r,n){if(n){r.style.opacity=n}else if(e==="toggle"){if(getComputedStyle(r).opacity==="0"){t.opacity("show",r,n)}else{t.opacity("hide",r,n)}}else if(e==="hide"){r.style.opacity="0"}else{r.style.opacity="1"}}};var n=function(e,t,r){var n;var i=r.currentToken();if(i.value==="when"||i.value==="with"||e.commandBoundary(i)){n=e.parseElement("implicitMeTarget",r)}else{n=e.parseElement("expression",r)}return n};var i=function(e,n,i){var a=r.defaultHideShowStrategy;var o=t;if(r.hideShowStrategies){o=Object.assign(o,r.hideShowStrategies)}i=i||a||"display";var s=o[i];if(s==null){e.raiseParseError(n,"Unknown show/hide strategy : "+i)}return s};e.addCommand("hide",(function(e,t,r){if(r.matchToken("hide")){var a=n(e,t,r);var o=null;if(r.matchToken("with")){o=r.requireTokenType("IDENTIFIER","STYLE_REF").value;if(o.indexOf("*")===0){o=o.substr(1)}}var s=i(e,r,o);return{target:a,args:[a],op:function(e,r){t.nullCheck(r,a);t.implicitLoop(r,(function(e){s("hide",e)}));return t.findNext(this,e)}}}}));e.addCommand("show",(function(e,t,r){if(r.matchToken("show")){var a=n(e,t,r);var o=null;if(r.matchToken("with")){o=r.requireTokenType("IDENTIFIER","STYLE_REF").value;if(o.indexOf("*")===0){o=o.substr(1)}}var s=null;if(r.matchOpToken(":")){var u=r.consumeUntilWhitespace();r.matchTokenType("WHITESPACE");s=u.map((function(e){return e.value})).join("")}if(r.matchToken("when")){var l=e.requireElement("expression",r)}var c=i(e,r,o);return{target:a,when:l,args:[a],op:function(e,r){t.nullCheck(r,a);t.implicitLoop(r,(function(r){if(l){e.result=r;let n=t.evaluateNoPromise(l,e);if(n){c("show",r,s)}else{c("hide",r)}e.result=null}else{c("show",r,s)}}));return t.findNext(this,e)}}}}));e.addCommand("take",(function(e,t,r){if(r.matchToken("take")){let u=null;let l=[];while(u=e.parseElement("classRef",r)){l.push(u)}var n=null;var i=null;let c=l.length>0;if(!c){n=e.parseElement("attributeRef",r);if(n==null){e.raiseParseError(r,"Expected either a class reference or attribute expression")}if(r.matchToken("with")){i=e.requireElement("expression",r)}}if(r.matchToken("from")){var a=e.requireElement("expression",r)}if(r.matchToken("for")){var o=e.requireElement("expression",r)}else{var o=e.requireElement("implicitMeTarget",r)}if(c){var s={classRefs:l,from:a,forElt:o,args:[l,a,o],op:function(e,r,n,i){t.nullCheck(i,o);t.implicitLoop(r,(function(e){var r=e.className;if(n){t.implicitLoop(n,(function(e){e.classList.remove(r)}))}else{t.implicitLoop(e,(function(e){e.classList.remove(r)}))}t.implicitLoop(i,(function(e){e.classList.add(r)}))}));return t.findNext(this,e)}};return s}else{var s={attributeRef:n,from:a,forElt:o,args:[a,o,i],op:function(e,r,i,s){t.nullCheck(r,a);t.nullCheck(i,o);t.implicitLoop(r,(function(e){if(!s){e.removeAttribute(n.name)}else{e.setAttribute(n.name,s)}}));t.implicitLoop(i,(function(e){e.setAttribute(n.name,n.value||"")}));return t.findNext(this,e)}};return s}}}));function a(t,r,n,i){if(n!=null){var a=t.resolveSymbol(n,r)}else{var a=r}if(a instanceof Element||a instanceof HTMLDocument){while(a.firstChild)a.removeChild(a.firstChild);a.append(e.runtime.convertValue(i,"Fragment"));t.processNode(a)}else{if(n!=null){t.setSymbol(n,r,null,i)}else{throw"Don't know how to put a value into "+typeof r}}}e.addCommand("put",(function(e,t,r){if(r.matchToken("put")){var n=e.requireElement("expression",r);var i=r.matchAnyToken("into","before","after");if(i==null&&r.matchToken("at")){r.matchToken("the");i=r.matchAnyToken("start","end");r.requireToken("of")}if(i==null){e.raiseParseError(r,"Expected one of 'into', 'before', 'at start of', 'at end of', 'after'")}var o=e.requireElement("expression",r);var s=i.value;var u=false;var l=false;var c=null;var f=null;if(o.type==="arrayIndex"&&s==="into"){u=true;f=o.prop;c=o.root}else if(o.prop&&o.root&&s==="into"){f=o.prop.value;c=o.root}else if(o.type==="symbol"&&s==="into"){l=true;f=o.name}else if(o.type==="attributeRef"&&s==="into"){var m=true;f=o.name;c=e.requireElement("implicitMeTarget",r)}else if(o.type==="styleRef"&&s==="into"){var p=true;f=o.name;c=e.requireElement("implicitMeTarget",r)}else if(o.attribute&&s==="into"){var m=o.attribute.type==="attributeRef";var p=o.attribute.type==="styleRef";f=o.attribute.name;c=o.root}else{c=o}var h={target:o,operation:s,symbolWrite:l,value:n,args:[c,f,n],op:function(e,r,n,i){if(l){a(t,e,n,i)}else{t.nullCheck(r,c);if(s==="into"){if(m){t.implicitLoop(r,(function(e){e.setAttribute(n,i)}))}else if(p){t.implicitLoop(r,(function(e){e.style[n]=i}))}else if(u){r[n]=i}else{t.implicitLoop(r,(function(e){a(t,e,n,i)}))}}else{var o=s==="before"?Element.prototype.before:s==="after"?Element.prototype.after:s==="start"?Element.prototype.prepend:s==="end"?Element.prototype.append:Element.prototype.append;t.implicitLoop(r,(function(e){o.call(e,i instanceof Node?i:t.convertValue(i,"Fragment"));if(e.parentElement){t.processNode(e.parentElement)}else{t.processNode(e)}}))}}return t.findNext(this,e)}};return h}}));function o(e,t,r){var n;if(r.matchToken("the")||r.matchToken("element")||r.matchToken("elements")||r.currentToken().type==="CLASS_REF"||r.currentToken().type==="ID_REF"||r.currentToken().op&&r.currentToken().value==="<"){e.possessivesDisabled=true;try{n=e.parseElement("expression",r)}finally{delete e.possessivesDisabled}if(r.matchOpToken("'")){r.requireToken("s")}}else if(r.currentToken().type==="IDENTIFIER"&&r.currentToken().value==="its"){var i=r.matchToken("its");n={type:"pseudopossessiveIts",token:i,name:i.value,evaluate:function(e){return t.resolveSymbol("it",e)}}}else{r.matchToken("my")||r.matchToken("me");n=e.parseElement("implicitMeTarget",r)}return n}e.addCommand("transition",(function(e,t,n){if(n.matchToken("transition")){var i=o(e,t,n);var a=[];var s=[];var u=[];var l=n.currentToken();while(!e.commandBoundary(l)&&l.value!=="over"&&l.value!=="using"){if(n.currentToken().type==="STYLE_REF"){let e=n.consumeToken();let t=e.value.substr(1);a.push({type:"styleRefValue",evaluate:function(){return t}})}else{a.push(e.requireElement("stringLike",n))}if(n.matchToken("from")){s.push(e.requireElement("expression",n))}else{s.push(null)}n.requireToken("to");if(n.matchToken("initial")){u.push({type:"initial_literal",evaluate:function(){return"initial"}})}else{u.push(e.requireElement("expression",n))}l=n.currentToken()}if(n.matchToken("over")){var c=e.requireElement("expression",n)}else if(n.matchToken("using")){var f=e.requireElement("expression",n)}var m={to:u,args:[i,a,s,u,f,c],op:function(e,n,a,o,s,u,l){t.nullCheck(n,i);var c=[];t.implicitLoop(n,(function(e){var n=new Promise((function(n,i){var c=e.style.transition;if(l){e.style.transition="all "+l+"ms ease-in"}else if(u){e.style.transition=u}else{e.style.transition=r.defaultTransition}var f=t.getInternalData(e);var m=getComputedStyle(e);var p={};for(var h=0;he.forEach((e=>S(e))))).then((()=>n((function(){a();k.processNode(document.documentElement);e.document.addEventListener("htmx:load",(function(e){k.processNode(e.detail.elt)}))}))));function n(e){if(document.readyState!=="loading"){setTimeout(e)}else{document.addEventListener("DOMContentLoaded",e)}}function i(){var e=document.querySelector('meta[name="htmx-config"]');if(e){return v(e.content)}else{return null}}function a(){var e=i();if(e){Object.assign(r,e)}}}const S=Object.assign(b,{config:r,use(e){e(S)},internals:{lexer:x,parser:g,runtime:k,Lexer:n,Tokens:i,Parser:a,Runtime:o},ElementCollection:m,addFeature:g.addFeature.bind(g),addCommand:g.addCommand.bind(g),addLeafExpression:g.addLeafExpression.bind(g),addIndirectExpression:g.addIndirectExpression.bind(g),evaluate:k.evaluate.bind(k),parse:k.parse.bind(k),processNode:k.processNode.bind(k),version:"0.9.12",browserInit:w});return S})); diff --git a/www/_redirects b/www/_redirects new file mode 100644 index 0000000..3f17236 --- /dev/null +++ b/www/_redirects @@ -0,0 +1 @@ +* https://htmx.org/extensions 301 diff --git a/www/index.html b/www/index.html deleted file mode 100644 index 0cf95a1..0000000 --- a/www/index.html +++ /dev/null @@ -1,475 +0,0 @@ - - - - htmx extensions - - - - - -
- -

</> htmx

-

extensions

-

- This site is a searchable collection of extensions for htmx 2.0. They are not - guaranteed to work with the htmx 1.x codebase. -

-

- Core extensions are actively maintained by the htmx team. -

-

- Community extensions are contributed by the community or rarely touched by the htmx - team (although they still work!) -

-
- Contributing - If you'd like to add your extension to this website, add a row to the table below in a PR against the - /www/index.html - file. -
- -

Core

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
- - sse - - - Provides support for - Server - Sent Events - directly from HTML. -
- - ws - - - Provides bi-directional communication with - Web - Sockets - servers directly from HTML -
- - head-support - - - Provides support for merging head tag information (styles, etc.) in htmx requests -
- - idiomorph - - - Provides a morph swap strategy based on the idiomorph - morphing library, which was created by the htmx team. -
- - htmx-1-compat - - - Rolls back most of the behavioral changes of htmx 2 to the htmx 1 defaults. -
- - preload - - - This extension allows you to load HTML fragments into your browser's cache before they are requested by - the user, so that additional pages appear to users to load nearly instantaneously. -
- - response-targets - - - This extension allows you to specify different target elements to be swapped when - different HTTP response codes are received. -
-

Community

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
- - ajax-header - - - Adds an X-Requested-With header to all requests made by htmx -
- - alpine-morph - - - Alpine.js now has a lightweight morph plugin and this extension - allows you to use it as the swapping mechanism in htmx which is necessary to retain Alpine state when you have entire Alpine components swapped by htmx. -
- - class-tools - - - The `class-tools` extension allows you to specify CSS classes that will be swapped onto or off of the elements by using - a `classes` or `data-classes` attribute. -
- - client-side-templates - - - This extension supports transforming a JSON/XML request response into HTML via a client-side template before it is - swapped into the DOM. -
- - debug - - - This extension includes log all htmx events for the element it is on, either through the `console.debug` function - or through the `console.log` function with a `DEBUG:` prefix. -
- - disable-element - - - This extension disables an element during an htmx request, when configured on the element triggering the - request. Note that this functionality is now part of the core of htmx via the hx-disabled-elt - attribute -
- - event-header - - - This extension adds the `Triggering-Event` header to requests. The value of - the header is a JSON serialized version of the event that triggered the - request. -
- - include-vals - - - The `include-vals` extension allows you to programmatically include values in a request with - a `include-vals` attribute. The value of this attribute is one or more name/value pairs, which - will be evaluated as the fields in a javascript object literal. -
- - json-enc - - - This extension encodes parameters in JSON format instead of url format. -
- - json-enc-custom - - - This extension works similarly to json-enc but allows for very complex structures, such as embedding JSON objects, lists, or handling indexes, just by using the name attribute. -
- - loading-states - - - This extension allows you to easily manage loading states while a request is in flight, including - disabling elements, and adding and removing CSS classes. -
- - morphdom-swap - - - Provides a morph swap strategy based on the morphdom morphing library. -
- - multi-swap - - - This extension allows you to swap multiple elements marked from the HTML response. You can also choose for - each element which swap method should be used. -
- - no-cache - - - This extension forces HTMX to bypass client caches and make a new request. An `hx-no-cache` header is also added to allow server-side caching to be bypassed. -
- - path-deps - - - This extension supports expressing inter-element dependencies based on paths, inspired by the - intercooler.js dependencies mechanism. -
- - path-params - - - This extension uses request parameters to populate path variables. Used parameters are removed so they - won't be sent in the query string or body anymore. -
- - preserve-attr - - - Preserves attributes while swaping content with swap type `outerHTML`. May be useful if you work with alpine.js.. -
- - remove-me - - - Allows you to remove an element after a specified interval. -
- - replace-params - - - This extension uses request parameters to replace existing parameters. If given value is empty string then parameter will be deleted. This extension would be useful in situations like pagination, search that you only want to replace only few parameters instead of reset all parameters. -
- - restored - - - Triggers an event whenever a back button even is detected while using hx-boost -
- - safe-nonce - - - The `safe-nonce` extension can be used to improve the security of the application/web-site and help avoid XSS issues by allowing you to return known trusted inline scripts safely -
- - Shoelace - - - Makes Shoelace elements work seamlessly (as possible) with htmx. -
- - signalr - - - Provides bidirectional real-time communication via SignalR. -
- -
-