From d26fe4709d71e05f304d65c5a2b02a90791a36a0 Mon Sep 17 00:00:00 2001 From: Alexander Petros Date: Thu, 12 Oct 2023 18:01:34 -0400 Subject: [PATCH] Remove the last of the go code (#1890) Update the website tests with the latest tests on master, deleting the mirrored go code in the process. --- www/static/src/ext/client-side-templates.js | 22 +- www/static/test/ext/client-side-templates.js | 8 +- www/static/test/realtime/README.md | 57 --- www/static/test/realtime/go.mod | 22 -- www/static/test/realtime/go.sum | 52 --- www/static/test/realtime/server.go | 350 ------------------ www/static/test/realtime/static/index.html | 52 --- .../test/realtime/static/sse-simple-ext.html | 26 -- .../test/realtime/static/sse-simple.html | 26 -- .../realtime/static/sse-triggers-ext.html | 26 -- .../test/realtime/static/sse-triggers.html | 26 -- www/static/test/servers/ws/README.md | 15 - www/static/test/servers/ws/go.mod | 23 -- www/static/test/servers/ws/go.sum | 52 --- www/static/test/servers/ws/server.go | 76 ---- .../servers/ws/static/black_transparent.svg | 9 - www/static/test/servers/ws/static/index.html | 63 ---- .../test/servers/ws/static/stylesheet.css | 112 ------ .../servers/ws/static/white_transparent.svg | 9 - www/static/test/ws-sse/README.md | 41 ++ www/static/test/ws-sse/server.js | 244 ++++++++++++ .../static/black_transparent.svg | 0 .../{realtime => ws-sse}/static/data.json | 0 www/static/test/ws-sse/static/index.html | 12 + www/static/test/ws-sse/static/site-base.html | 37 ++ .../static/sse-about.html | 2 + .../static/sse-multichannel-ext.html | 10 +- .../static/sse-multichannel.html | 10 +- .../static/sse-multiple-ext.html | 12 +- .../static/sse-multiple.html | 12 +- .../static/sse-settle-ext.html | 10 +- .../static/sse-settle.html | 10 +- .../test/ws-sse/static/sse-simple-ext.html | 28 ++ www/static/test/ws-sse/static/sse-simple.html | 28 ++ .../static/sse-target-ext.html | 8 +- .../static/sse-target.html | 8 +- .../test/ws-sse/static/sse-triggers-ext.html | 28 ++ .../test/ws-sse/static/sse-triggers.html | 28 ++ .../static/stylesheet.css | 4 +- .../static/white_transparent.svg | 0 .../{realtime => ws-sse}/static/ws-about.html | 2 + .../static/ws-echo-ext.html | 10 +- .../{realtime => ws-sse}/static/ws-echo.html | 10 +- .../static/ws-heartbeat-ext.html | 10 +- .../static/ws-heartbeat.html | 12 +- .../static/ws-reconnect-ext.html | 0 .../static/ws-reconnect.html | 0 47 files changed, 546 insertions(+), 1056 deletions(-) delete mode 100644 www/static/test/realtime/README.md delete mode 100644 www/static/test/realtime/go.mod delete mode 100644 www/static/test/realtime/go.sum delete mode 100644 www/static/test/realtime/server.go delete mode 100644 www/static/test/realtime/static/index.html delete mode 100644 www/static/test/realtime/static/sse-simple-ext.html delete mode 100644 www/static/test/realtime/static/sse-simple.html delete mode 100644 www/static/test/realtime/static/sse-triggers-ext.html delete mode 100644 www/static/test/realtime/static/sse-triggers.html delete mode 100644 www/static/test/servers/ws/README.md delete mode 100644 www/static/test/servers/ws/go.mod delete mode 100644 www/static/test/servers/ws/go.sum delete mode 100644 www/static/test/servers/ws/server.go delete mode 100644 www/static/test/servers/ws/static/black_transparent.svg delete mode 100644 www/static/test/servers/ws/static/index.html delete mode 100644 www/static/test/servers/ws/static/stylesheet.css delete mode 100644 www/static/test/servers/ws/static/white_transparent.svg create mode 100644 www/static/test/ws-sse/README.md create mode 100644 www/static/test/ws-sse/server.js rename www/static/test/{realtime => ws-sse}/static/black_transparent.svg (100%) rename www/static/test/{realtime => ws-sse}/static/data.json (100%) create mode 100644 www/static/test/ws-sse/static/index.html create mode 100644 www/static/test/ws-sse/static/site-base.html rename www/static/test/{realtime => ws-sse}/static/sse-about.html (98%) rename www/static/test/{realtime => ws-sse}/static/sse-multichannel-ext.html (78%) rename www/static/test/{realtime => ws-sse}/static/sse-multichannel.html (79%) rename www/static/test/{realtime => ws-sse}/static/sse-multiple-ext.html (55%) rename www/static/test/{realtime => ws-sse}/static/sse-multiple.html (55%) rename www/static/test/{realtime => ws-sse}/static/sse-settle-ext.html (83%) rename www/static/test/{realtime => ws-sse}/static/sse-settle.html (84%) create mode 100644 www/static/test/ws-sse/static/sse-simple-ext.html create mode 100644 www/static/test/ws-sse/static/sse-simple.html rename www/static/test/{realtime => ws-sse}/static/sse-target-ext.html (87%) rename www/static/test/{realtime => ws-sse}/static/sse-target.html (63%) create mode 100644 www/static/test/ws-sse/static/sse-triggers-ext.html create mode 100644 www/static/test/ws-sse/static/sse-triggers.html rename www/static/test/{realtime => ws-sse}/static/stylesheet.css (98%) rename www/static/test/{realtime => ws-sse}/static/white_transparent.svg (100%) rename www/static/test/{realtime => ws-sse}/static/ws-about.html (98%) rename www/static/test/{realtime => ws-sse}/static/ws-echo-ext.html (81%) rename www/static/test/{realtime => ws-sse}/static/ws-echo.html (82%) rename www/static/test/{realtime => ws-sse}/static/ws-heartbeat-ext.html (64%) rename www/static/test/{realtime => ws-sse}/static/ws-heartbeat.html (64%) rename www/static/test/{realtime => ws-sse}/static/ws-reconnect-ext.html (100%) rename www/static/test/{realtime => ws-sse}/static/ws-reconnect.html (100%) diff --git a/www/static/src/ext/client-side-templates.js b/www/static/src/ext/client-side-templates.js index 64e3ce88..2bace419 100644 --- a/www/static/src/ext/client-side-templates.js +++ b/www/static/src/ext/client-side-templates.js @@ -28,15 +28,27 @@ htmx.defineExtension('client-side-templates', { var handlebarsTemplate = htmx.closest(elt, "[handlebars-template]"); if (handlebarsTemplate) { var data = JSON.parse(text); - var templateName = handlebarsTemplate.getAttribute('handlebars-template'); - return Handlebars.partials[templateName](data); + var templateId = handlebarsTemplate.getAttribute('handlebars-template'); + var templateElement = htmx.find('#' + templateId).innerHTML; + var renderTemplate = Handlebars.compile(templateElement); + if (renderTemplate) { + return renderTemplate(data); + } else { + throw "Unknown handlebars template: " + templateId; + } } var handlebarsArrayTemplate = htmx.closest(elt, "[handlebars-array-template]"); if (handlebarsArrayTemplate) { var data = JSON.parse(text); - var templateName = handlebarsArrayTemplate.getAttribute('handlebars-array-template'); - return Handlebars.partials[templateName]({"data": data}); + var templateId = handlebarsArrayTemplate.getAttribute('handlebars-array-template'); + var templateElement = htmx.find('#' + templateId).innerHTML; + var renderTemplate = Handlebars.compile(templateElement); + if (renderTemplate) { + return renderTemplate(data); + } else { + throw "Unknown handlebars template: " + templateId; + } } var nunjucksTemplate = htmx.closest(elt, "[nunjucks-template]"); @@ -50,7 +62,7 @@ htmx.defineExtension('client-side-templates', { return nunjucks.render(templateName, data); } } - + var xsltTemplate = htmx.closest(elt, "[xslt-template]"); if (xsltTemplate) { var templateId = xsltTemplate.getAttribute('xslt-template'); diff --git a/www/static/test/ext/client-side-templates.js b/www/static/test/ext/client-side-templates.js index 3a808a65..c6fdd326 100644 --- a/www/static/test/ext/client-side-templates.js +++ b/www/static/test/ext/client-side-templates.js @@ -29,16 +29,16 @@ describe("client-side-templates extension", function() { it('works on basic handlebars template', function () { this.server.respondWith("GET", "/test", '{"foo":"bar"}'); var btn = make('') - Handlebars.partials["hb1"] = Handlebars.compile("*{{foo}}*"); + make('') btn.click(); this.server.respond(); btn.innerHTML.should.equal("*bar*"); }); it('works on handlebars array template', function () { - this.server.respondWith("GET", "/test", '{"foo":"bar"}'); + this.server.respondWith("GET", "/test", '[{"foo":"bar"}]'); var btn = make('') - Handlebars.partials["hb1"] = Handlebars.compile("*{{data.foo}}*"); + make('') btn.click(); this.server.respond(); btn.innerHTML.should.equal("*bar*"); @@ -56,4 +56,4 @@ describe("client-side-templates extension", function() { this.server.respond(); btn.innerHTML.should.equal("*bar*"); }); -}); \ No newline at end of file +}); diff --git a/www/static/test/realtime/README.md b/www/static/test/realtime/README.md deleted file mode 100644 index 0f9b1c17..00000000 --- a/www/static/test/realtime/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Htmx - Realtime Test Suite - -This package implements a realtime server for testing WebSockets and Server Sent Events (SSE) in htmx. - -## How to Use This Server - -1. If you do not already have Go (version 1.17 or higher) installed on your machine, you can download an installation for your machine from [https://golang.org](the Go website) - -2. Open up a terminal window and navigate to this directory. Start up the WebSocket server by typing `go run server.go` - -3. Your browser should open the test suite web page automatically. If it doesn't, then navigate to [http://localhost](http://localhost) to run the manual tests. Huzzah! - -## Web Sockets - -This listens for incoming WebSocket connections coming in to ws://localhost:1323/echo and ws://localhost:1323/heartbeat. When it receives messages from any WebSocket client, it responds with that same content in a way that htmx can process. This means, that the response message will look like this: `
{your message here}
` - -### Echo - -The echo endpoint listens for incoming WebSocket connections coming in to `ws://localhost:1323/echo`. When it receives messages from any WebSocket client, it responds with that same content wrapped as an OOB Swap. So, if you post the message `Hello There. General Kenobi.` the server will respond with this: `
Hello There. General Kenobi.
` - -### Heartbeat - -The heartbeat endpoint `ws://localhost:1323/heartbeat`. It does not process any messages that are sent to it, but it does send messages containing random numbers to every listener at random intervals. Heartbeat message will look like this: `
12345678901234567890
` - -## Server Sent Events - -This package implements a simple server that generates Server Sent Events for your test pages to read. It streams fake data from [jsonplaceholder](https://jsonplaceholder.typicode.com) to your website on a semi-regular schedule. - -### JSON Event Streams - -Streams random JSON records every second (or so) to your client. - -* `/posts.json` -* `/comments.json` -* `/albums.json` -* `/photos.json` -* `/todos.json` -* `/users.json` - -### HTML Event Streams - -Streams random HTML fragments every second (or so) to your client. These streams are used by the manual htmx tests. - -* `/posts.html` -* `/comments.html` -* `/albums.html` -* `/photos.html` -* `/todos.html` -* `/users.html` - -### Specifying Event Types - -You can add a `type=` parameter to your URLs to specify the event name(s) that you want the server to use. You can specify multiple names in a comma separated list and the server will alternate between them. If you do not specify a type, then the default message name of `message` is used. - -## Credits - -It is inspired by [jsonplaceholder](https://jsonplaceholder.typicode.com) -- *"a free online REST API that you can use whenever you need some fake data."* diff --git a/www/static/test/realtime/go.mod b/www/static/test/realtime/go.mod deleted file mode 100644 index fa01fd9b..00000000 --- a/www/static/test/realtime/go.mod +++ /dev/null @@ -1,22 +0,0 @@ -module github.com/bigskysoftware/htmx/test/realtime - -go 1.17 - -require ( - github.com/benpate/derp v0.20.0 - github.com/benpate/htmlconv v0.3.0 - github.com/labstack/echo/v4 v4.9.0 - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 - golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f -) - -require ( - github.com/labstack/gommon v0.3.1 // indirect - github.com/mattn/go-colorable v0.1.11 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.1 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect - golang.org/x/text v0.3.7 // indirect -) diff --git a/www/static/test/realtime/go.sum b/www/static/test/realtime/go.sum deleted file mode 100644 index aeb442d8..00000000 --- a/www/static/test/realtime/go.sum +++ /dev/null @@ -1,52 +0,0 @@ -github.com/benpate/derp v0.20.0 h1:birPylsYOKkSMrcvNQEp+CmB7Dr1jnum8UXKv4oJvY8= -github.com/benpate/derp v0.20.0/go.mod h1:ENjMpkMmxn9gAulMAggElVcD2kNrsbjFhoZK8fUlCxY= -github.com/benpate/htmlconv v0.3.0 h1:UzIONnBJWtzBB3pYfKxlkq5JCsMq/+ImpAqNEmvFGcQ= -github.com/benpate/htmlconv v0.3.0/go.mod h1:9P8tQ62E5L/KF2wTNI0szRTKs3b9WlOHj4Gxmp66rf4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY= -github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= -github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= -github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/www/static/test/realtime/server.go b/www/static/test/realtime/server.go deleted file mode 100644 index fb847219..00000000 --- a/www/static/test/realtime/server.go +++ /dev/null @@ -1,350 +0,0 @@ -package main - -import ( - "bytes" - _ "embed" - "encoding/json" - "fmt" - "html/template" - "log" - "math/rand" - "net/http" - "strconv" - "strings" - "time" - - "github.com/benpate/derp" - "github.com/benpate/htmlconv" - "github.com/labstack/echo/v4" - "github.com/pkg/browser" - "golang.org/x/net/websocket" -) - -type formatFunc func(interface{}) string - -//go:embed static/data.json -var dataBytes []byte - -func main() { - - rand.Seed(time.Now().UnixNano()) - - /// Load configuration file - var data map[string][]interface{} - - if err := json.Unmarshal(dataBytes, &data); err != nil { - panic("Could not unmarshal data: " + err.Error()) - } - - /// Configure Web Server - - e := echo.New() - - e.Static("/", "static") - e.Static("/htmx", "../../src") - - // Web Socket Handlers - e.GET("/echo", wsEcho) - e.GET("/heartbeat", wsHeartbeat) - - // SSE - JSON Event Streams - e.GET("/posts.json", handleStream(makeStream(data["posts"], jsonFormatFunc))) - e.GET("/comments.json", handleStream(makeStream(data["comments"], jsonFormatFunc))) - e.GET("/photos.json", handleStream(makeStream(data["comments"], jsonFormatFunc))) - e.GET("/albums.json", handleStream(makeStream(data["albums"], jsonFormatFunc))) - e.GET("/todos.json", handleStream(makeStream(data["todos"], jsonFormatFunc))) - e.GET("/users.json", handleStream(makeStream(data["users"], jsonFormatFunc))) - - // SSE - HTML Event Streams (with HTMX extension tags) - e.GET("/posts.html", handleStream(makeStream(data["posts"], postTemplate()))) - e.GET("/comments.html", handleStream(makeStream(data["comments"], commentTemplate()))) - e.GET("/photos.json", handleStream(makeStream(data["comments"], jsonFormatFunc))) - e.GET("/albums.html", handleStream(makeStream(data["albums"], albumTemplate()))) - e.GET("/todos.html", handleStream(makeStream(data["todos"], todoTemplate()))) - e.GET("/users.html", handleStream(makeStream(data["users"], userTemplate()))) - - e.OPTIONS("/page/random", func(ctx echo.Context) error { - ctx.Response().Header().Add("Connection", "keep-alive") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Origin", "*") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Methods", "GET") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Credentials", "true") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Headers", "*") // CORS headers - ctx.NoContent(200) - return nil - }) - - e.GET("/page/random", func(ctx echo.Context) error { - return pageHandler(ctx, rand.Int()) - }) - - e.GET("/page/:number", func(ctx echo.Context) error { - - pageNumber, err := strconv.Atoi(ctx.Param("number")) - - if err != nil { - pageNumber = 1 - } - - return pageHandler(ctx, pageNumber) - }) - - e.GET("/revealed/:number", func(ctx echo.Context) error { - - pageNumber, err := strconv.Atoi(ctx.Param("number")) - - if err != nil { - pageNumber = 1 - } - - thisPage := strconv.Itoa(pageNumber) - nextPage := strconv.Itoa(pageNumber + 1) - random := strconv.Itoa(rand.Int()) - - template := htmlconv.CollapseWhitespace(` -
- This is page %s

- Randomly generated HTML %s

- I wish I were a haiku. -
`) - - content := fmt.Sprintf(template, nextPage, thisPage, random) - return ctx.HTML(200, content) - }) - - // On first run, open web browser in admin mode - browser.OpenURL("http://localhost/") - - e.Logger.Fatal(e.Start(":80")) -} - -/******************************************* - * Web Socket Handlers - *******************************************/ - -func wsHeartbeat(c echo.Context) error { - - handler := websocket.Handler(func(ws *websocket.Conn) { - - defer ws.Close() - - for i := 0; ; i = i + 1 { - - time.Sleep(1 * time.Second) - - random := rand.Int() - message := `
Message ` + strconv.Itoa(i) + `: ` + strconv.Itoa(random) + `
` - - if err := websocket.Message.Send(ws, message); err != nil { - c.Logger().Error("send", err) - return - } - } - }) - - handler.ServeHTTP(c.Response(), c.Request()) - return nil -} - -func wsEcho(c echo.Context) error { - - handler := websocket.Handler(func(ws *websocket.Conn) { - - defer ws.Close() - - for { - - msg := "" - - if err := websocket.Message.Receive(ws, &msg); err != nil { - c.Logger().Error("receive", err) - return - } - - response := `
` + msg + `
` - - if err := websocket.Message.Send(ws, response); err != nil { - c.Logger().Error("send", err) - return - } - } - }) - - handler.ServeHTTP(c.Response(), c.Request()) - return nil -} - -/******************************************* - * SSE Handlers - *******************************************/ - -func pageHandler(ctx echo.Context, page int) error { - - pageString := strconv.Itoa(page) - random := strconv.Itoa(rand.Int()) - - template := htmlconv.CollapseWhitespace(` -
- This is page %s

- Randomly generated HTML %s

- I wish I were a haiku. -
`) - - content := fmt.Sprintf(template, pageString, random) - ctx.Response().Header().Add("Access-Control-Allow-Origin", "*") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Methods", "GET") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Headers", "*") // CORS headers - ctx.Response().Header().Add("Access-Control-Allow-Credentials", "true") // CORS headers - return ctx.HTML(200, content) -} - -func postTemplate() formatFunc { - - // return templateFormatFunc("post.html", "DDD") - return templateFormatFunc("post.html", ` -
-
Post: {{.title}}
-
{{.body}}
-
id: {{.id}}
-
user: {{.userId}}
-
event: [[eventType]]
-
`) -} - -func commentTemplate() formatFunc { - - // return templateFormatFunc("comment.html", "CCC") - return templateFormatFunc("comment.html", ` -
-
Comment: {{.name}}
-
{{.email}}
-
{{.body}}
-
event: [[eventType]]
-
`) -} - -func albumTemplate() formatFunc { - - return templateFormatFunc("album.html", ` -
-
Album: {{.title}}
-
id: {{.id}}
-
event: [[eventType]]
-
`) -} - -func todoTemplate() formatFunc { - - return templateFormatFunc("todo.html", ` -
-
ToDo:{{.id}}: {{.title}}
-
complete? {{.completed}}
-
event: [[eventType]]
-
`) -} - -func userTemplate() formatFunc { - - return templateFormatFunc("user.html", ` -
-
User: {{.name}} / {{.username}}
-
{{.email}}
-
{{.address.street}} {{.address.suite}}
{{.address.city}}, {{.address.zipcode}}
-
event: [[eventType]]
-
`) -} - -// handleStream creates an echo.HandlerFunc that streams events from an eventSource to client browsers -func handleStream(eventSource chan string) echo.HandlerFunc { - - return func(ctx echo.Context) error { - - w := ctx.Response().Writer - - // Make sure that the writer supports flushing. - f, ok := w.(http.Flusher) - - if !ok { - return derp.New(500, "handler.ServerSentEvent", "Streaming Not Supported") - } - - c := ctx.Request().Context() - - // Set the headers related to event streaming. - w.Header().Set("Content-Type", "text/event-stream") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Connection", "keep-alive") - w.Header().Set("Transfer-Encoding", "chunked") - w.Header().Set("Access-Control-Allow-Origin", "*") - - types := []string{} - - if param := ctx.QueryParam("types"); param != "" { - types = strings.Split(param, ",") - } - - // Don't close the connection, instead loop endlessly. - for { - - select { - case <-c.Done(): - log.Println("HTTP connection just closed.") - return nil - - case message := <-eventSource: - - var eventType string - - if len(types) > 0 { - eventType = types[rand.Int()%len(types)] - fmt.Fprintf(w, "event: %s\n", eventType) - } - - message = strings.Replace(message, "\n", " ", -1) - message = strings.Replace(message, "[[eventType]]", eventType, 1) - - fmt.Fprintf(w, "data: %s\n\n", message) - - // Flush the response. This is only possible if the response supports streaming. - f.Flush() - } - } - } -} - -// makeStream loops through an array of interfaces -func makeStream(data []interface{}, format formatFunc) chan string { - - result := make(chan string) - - go func() { - - for { - for _, record := range data { - result <- format(record) - time.Sleep((time.Duration(rand.Int() % 500)) * time.Millisecond) - - } - } - }() - - return result -} - -func jsonFormatFunc(data interface{}) string { - result, _ := json.Marshal(data) - return string(result) -} - -func templateFormatFunc(name string, text string) formatFunc { - - f, _ := template.New(name).Parse(htmlconv.CollapseWhitespace(text)) - - return func(data interface{}) string { - - var buffer bytes.Buffer - - f.Execute(&buffer, data) - - return buffer.String() - } -} diff --git a/www/static/test/realtime/static/index.html b/www/static/test/realtime/static/index.html deleted file mode 100644 index 5d18333d..00000000 --- a/www/static/test/realtime/static/index.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - </> htmx Realtime Test Server - - - - - - - - - - - -
-

Realtime Test Server

- -

New Extensions

-

As of version 1.7, we have created two new extensions ws.js and sse.js to support realtime development in htmx. All new effort on WebSockets and Server Sent Events will occur in these extensions.

- -

Old Tags Deprecated

-

The existing hx-ws and hx-sse tags have been deprecated and will not receive any more updates. We plan to remove these two tags from the core library in htmx version 2.0.

- -

Try It For Yourself

-

Because extensions use a different calling syntax, there are minor differences in the way that this new code is invoked. This test server includes several demos / manual tests for each extension that you can try out for yourself. Each is presented side-by-side with test cases for the original code so that you can see the difference.

-
- - - \ No newline at end of file diff --git a/www/static/test/realtime/static/sse-simple-ext.html b/www/static/test/realtime/static/sse-simple-ext.html deleted file mode 100644 index 8c785af7..00000000 --- a/www/static/test/realtime/static/sse-simple-ext.html +++ /dev/null @@ -1,26 +0,0 @@ -

Simple Test

- -
- Legacy Style - New Style -
- -

Description

-

- This page connects to several different Server Sent Event (SSE) streams, listening on the default event name "message". - Each stream should populate its own container. -

-

Example HTML

-
-<div hx-ext="sse" sse-connect="http://localhost/posts.html" sse-swap="message">Waiting for Posts...</div>
-
- -

Test Cases

- -
-
Waiting for Posts...
-
Waiting for Comments...
-
Waiting for Albums...
-
Waiting for ToDos...
-
Waiting for Users...
-
\ No newline at end of file diff --git a/www/static/test/realtime/static/sse-simple.html b/www/static/test/realtime/static/sse-simple.html deleted file mode 100644 index a1233e0e..00000000 --- a/www/static/test/realtime/static/sse-simple.html +++ /dev/null @@ -1,26 +0,0 @@ -

Simple Test

- -
- Legacy Style - New Style -
- -

Description

-

- This page connects to several different Server Sent Event (SSE) streams, listening on the default event name "message". - Each stream should populate its own container. -

-

Example HTML

-
-<div hx-sse="connect:http://localhost/posts.html swap:message">Waiting for Posts...</div>
-
- -

Test Cases

- -
-
Waiting for Posts...
-
Waiting for Comments...
-
Waiting for Albums...
-
Waiting for ToDos...
-
Waiting for Users...
-
\ No newline at end of file diff --git a/www/static/test/realtime/static/sse-triggers-ext.html b/www/static/test/realtime/static/sse-triggers-ext.html deleted file mode 100644 index 87237caa..00000000 --- a/www/static/test/realtime/static/sse-triggers-ext.html +++ /dev/null @@ -1,26 +0,0 @@ -

Event Trigger Test

- -
- Legacy Style - New Style -
- -

Description

-

- This page connects to a single different Server Sent Event (SSE) streams, listening on events named "Event1", "Event2", "Event3", and "Event4". - Each event is used as a trigger for hx-get to load a random page from the server. -

-

Example HTML

-
-<div hx-ext="sse" sse-connect="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
-	<div hx-get="http://localhost/page/random" hx-trigger="sse:Event1">Waiting for Posts...</div>
-</div>
-
- -
-

Test Cases

-
Waiting for Posts in Event1 channel...
-
Waiting for Posts in Event2 channel...
-
Waiting for Posts in Event3 channel...
-
Waiting for Posts in Event4 channel...
-
diff --git a/www/static/test/realtime/static/sse-triggers.html b/www/static/test/realtime/static/sse-triggers.html deleted file mode 100644 index 98c49631..00000000 --- a/www/static/test/realtime/static/sse-triggers.html +++ /dev/null @@ -1,26 +0,0 @@ -

Event Trigger Test

- -
- Legacy Style - New Style -
- -

Description

-

- This page connects to a single different Server Sent Event (SSE) streams, listening on events named "Event1", "Event2", "Event3", and "Event4". - Each event is used as a trigger for hx-get to load a random page from the server. -

-

Example HTML

-
-<div hx-sse="connect:http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
-	<div hx-get="http://localhost/page/random" hx-trigger="sse:Event1">Waiting for Posts...</div>
-</div>
-
- -
-

Test Cases

-
Waiting for Posts in Event1 channel...
-
Waiting for Posts in Event2 channel...
-
Waiting for Posts in Event3 channel...
-
Waiting for Posts in Event4 channel...
-
diff --git a/www/static/test/servers/ws/README.md b/www/static/test/servers/ws/README.md deleted file mode 100644 index b12c91ab..00000000 --- a/www/static/test/servers/ws/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Htmx Realtime Test Server - -This package implements a realtime server for testing WebSockets and Server Sent Events (SSE) in htmx. - -## What It Does - -This server listens for incoming WebSocket connections coming in to ws://localhost:1323/echo. When it receives messages from any WebSocket client, it responds with that same content in a way that htmx can process. This means, that the response message will look like this: `
{your message here}
` - -## How to Use This Server - -1. If you do not already have Go (version 1.17 or higher) installed on your machine, you can download an installation for your machine from [the Go website](https://golang.org) - -2. Open up a terminal window and navigate to this directory. Start up the WebSocket server by typing `go run server.go` - -3. Open your web browser to [http://localhost](http://localhost) to run the manual tests. Huzzah! diff --git a/www/static/test/servers/ws/go.mod b/www/static/test/servers/ws/go.mod deleted file mode 100644 index 0b30769d..00000000 --- a/www/static/test/servers/ws/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -module github.com/benpate/ghost - -go 1.17 - -require ( - github.com/labstack/echo/v4 v4.3.0 - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/labstack/gommon v0.3.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/stretchr/testify v1.7.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.1 // indirect - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect - golang.org/x/text v0.3.6 // indirect - golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect -) diff --git a/www/static/test/servers/ws/go.sum b/www/static/test/servers/ws/go.sum deleted file mode 100644 index 731271f9..00000000 --- a/www/static/test/servers/ws/go.sum +++ /dev/null @@ -1,52 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/labstack/echo/v4 v4.3.0 h1:DCP6cbtT+Zu++K6evHOJzSgA2115cPMuCx0xg55q1EQ= -github.com/labstack/echo/v4 v4.3.0/go.mod h1:PvmtTvhVqKDzDQy4d3bWzPjZLzom4iQbAZy2sgZ/qI8= -github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/www/static/test/servers/ws/server.go b/www/static/test/servers/ws/server.go deleted file mode 100644 index 5d06c5a1..00000000 --- a/www/static/test/servers/ws/server.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "math/rand" - "strconv" - "time" - - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "golang.org/x/net/websocket" -) - -func wsHeartbeat(c echo.Context) error { - - handler := websocket.Handler(func(ws *websocket.Conn) { - - defer ws.Close() - - for i := 0; ; i = i + 1 { - - time.Sleep(1 * time.Second) - - random := rand.Int() - message := `
Message ` + strconv.Itoa(i) + `: ` + strconv.Itoa(random) + `
` - - if err := websocket.Message.Send(ws, message); err != nil { - c.Logger().Error("send", err) - return - } - } - }) - - handler.ServeHTTP(c.Response(), c.Request()) - return nil -} - -func wsEcho(c echo.Context) error { - - handler := websocket.Handler(func(ws *websocket.Conn) { - - defer ws.Close() - - for { - - msg := "" - - if err := websocket.Message.Receive(ws, &msg); err != nil { - c.Logger().Error("receive", err) - return - } - - response := `
` + msg + `
` - - if err := websocket.Message.Send(ws, response); err != nil { - c.Logger().Error("send", err) - return - } - } - }) - - handler.ServeHTTP(c.Response(), c.Request()) - return nil -} - -func main() { - e := echo.New() - e.Use(middleware.Logger()) - e.Use(middleware.Recover()) - - e.Static("/", "./static") - e.Static("/htmx", "../../../src") - - e.GET("/echo", wsEcho) - e.GET("/heartbeat", wsHeartbeat) - e.Logger.Fatal(e.Start(":80")) -} diff --git a/www/static/test/servers/ws/static/black_transparent.svg b/www/static/test/servers/ws/static/black_transparent.svg deleted file mode 100644 index 99db1a26..00000000 --- a/www/static/test/servers/ws/static/black_transparent.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/www/static/test/servers/ws/static/index.html b/www/static/test/servers/ws/static/index.html deleted file mode 100644 index c2d3314b..00000000 --- a/www/static/test/servers/ws/static/index.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - </> htmx WebSocket Server - - - - - - - - - - -
-

WebSockets Extension Tests

- -

As of version 1.7, WebSocket support has been moved out of the core htmx library and into an extension. This server runs a test suite for the htmx WebSocket extension.

-

This extension connects to a WebSocket echo server and can send and receive messages to and from the server.

- -

Required Attributes

- - - - - - - - - - - - - -
hx-extMake sure the SSE extension is initialized on every page or page fragment where you use SSE streams.
ws-connectConnects to a WebSocket server. Attribute value must begin with ws:// wss://
ws-sendAdd to a form to submit form data to the websocket server instead of to an HTTP server.
- -

Example Code

- -
-<body hx-ext="ws">
-    <div ws-connect="wss://my.websocket.server.com"></div>
-
-	<form ws-send>
-		<input name="WebSocketMessage">
-	<form/>
-</body>
-
-

WebSocket Resources

- -
- - \ No newline at end of file diff --git a/www/static/test/servers/ws/static/stylesheet.css b/www/static/test/servers/ws/static/stylesheet.css deleted file mode 100644 index adccb158..00000000 --- a/www/static/test/servers/ws/static/stylesheet.css +++ /dev/null @@ -1,112 +0,0 @@ -*{ - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - box-sizing: border-box; -} - -body { - background-color: white; - padding:0px; - margin:0px; - width:100%; -} - -#header { - width:100%; - height: 100px; - background-image:url('white_transparent.svg'); - background-position:left 50px center; - background-repeat:no-repeat; - background-size: 300px; - background-color:black; -} - -#navigation { - position:absolute; - width:150px; - margin-top:50px; - margin-left:20px; - white-space: nowrap; -} - -#navigation > a { - display:block; - cursor: pointer; - text-decoration:none; - padding:10px 20px; -} - -#navigation > a:hover { - background-color:#eee; -} - -#navigation > a.selected { - font-weight:bold; -} - -#page { - margin: 50px; - padding-left:150px; -} - -.container { - padding: 10px; - border: solid 1px gray; - margin-bottom: 20px; - background-color:#f7f7f7; -} - -.container.htmx-settling { - border:solid 3px red!important; - padding:8px!important; -} - -pre.code { - font-family:'Courier New', Courier, monospace; - background-color: #444440; - color: #0f0; - padding:30px 5px 30px 15px; - overflow-y:scroll; - display:block; -} - -.bold { - font-weight:bold; -} - -.nowrap { - white-space: nowrap; -} - -table { - border-collapse: collapse; -} - -td { - padding:10px 20px; - border:solid 1px #ddd; - vertical-align: top; -} - -.demo { - padding:10px; - margin:20px 0px; - color:white; - background-color: #999; - height:100px; -} - -a, a:visited { - color:#3465a4; -} - - -.btn { - padding:5px 10px; - border:none; - border-radius:5px; -} - -.btn.primary { - background-color:#3465a4; - color:white; -} \ No newline at end of file diff --git a/www/static/test/servers/ws/static/white_transparent.svg b/www/static/test/servers/ws/static/white_transparent.svg deleted file mode 100644 index be885d68..00000000 --- a/www/static/test/servers/ws/static/white_transparent.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/www/static/test/ws-sse/README.md b/www/static/test/ws-sse/README.md new file mode 100644 index 00000000..5da1e13d --- /dev/null +++ b/www/static/test/ws-sse/README.md @@ -0,0 +1,41 @@ +# WebSocket and Server-Side Events live test suite +This package implements a realtime server for testing WebSockets and Server Sent Events (SSE) in htmx. + +## How to Use This Server +From this directory, run `node server.js`. + +If you want to hotreload, I recommend using `nodemon`, which you can install with `npm install -g +nodemon`, and then run `nodemon -e js,html server.js`. + +## Web Sockets +When htmx receives messages from any WebSocket client, it responds with that same content in a way that htmx can process. This means, that the response message will look like this: `
{your message here}
` + +### Echo +When the echo endpoint receives messages from any WebSocket client, it responds with that same content wrapped as an OOB Swap. So, if you post the message `Hello There. General Kenobi.` the server will respond with this: `
Hello There. General Kenobi.
` + +### Heartbeat +The heartbeat endpoint does not process any messages that are sent to it, but it does send messages containing random numbers to every listener at random intervals. Heartbeat message will look like this: `
12345678901234567890
` + +## Server Sent Events + +This package implements a simple server that generates Server Sent Events for your test pages to read. It streams fake data from [jsonplaceholder](https://jsonplaceholder.typicode.com) to your website on a semi-regular schedule. + +### HTML Event Streams + +Streams random HTML fragments every second (or so) to your client. These streams are used by the manual htmx tests. + +* `/posts.html` +* `/comments.html` +* `/albums.html` +* `/photos.html` +* `/todos.html` +* `/users.html` + +### Specifying Event Types + +You can add a `type=` parameter to your URLs to specify the event name(s) that you want the server to use. You can specify multiple names in a comma separated list and the server will alternate between them. If you do not specify a type, then the default message name of `message` is used. + +## Credits +This test suite was originally written by Ben Pate, and updated to run in nodeJS by Alex Petros. + +It is inspired by [jsonplaceholder](https://jsonplaceholder.typicode.com) -- *"a free online REST API that you can use whenever you need some fake data."* diff --git a/www/static/test/ws-sse/server.js b/www/static/test/ws-sse/server.js new file mode 100644 index 00000000..0cbe53c3 --- /dev/null +++ b/www/static/test/ws-sse/server.js @@ -0,0 +1,244 @@ +import * as http from 'node:http' +import * as path from 'node:path' +import * as fs from 'node:fs/promises' + +import { WebSocketServer } from 'ws' + +// Define some string and number constants +const HOSTNAME = '127.0.0.1'; +const PORT = 8080; +const DATA = JSON.parse(await fs.readFile('./static/data.json')) +const SITE_BASE = (await fs.readFile('./static/site-base.html')).toString() + +// Define the websockets +const ECHO_WS = createWebSocket((ws) => { + ws.on('message', (message) => { + const data = JSON.parse(message.toString()) + ws.send(`
${data.message}
`) + }) +}) +const HEARTBEAT_WS = createWebSocket((ws) => { + ws.interval = setInterval(() => { + const num = Math.trunc(Math.random() * 10**10) + ws.send(`
${num}
`) + }, 1000) +}, (ws) => clearInterval(ws.interval)) + + +// Define the server +const server = http.createServer(async (req, res) => { + try { + await handleRequest(req, res) + } catch (error) { + console.error(`Error serving ${req.url}`) + console.error(req.body) + console.error(error) + } +}) + +// This handles all the non-websocket requests +async function handleRequest (req, res) { + // If the URL starts with htmx, serve the src/ root version of htmx + if (req.url.startsWith('/htmx')) { + const resource = req.url.substring(6) + res.setHeader('Content-Type', 'text/javascript') + const fp = path.join('../../src', resource) + return serveFile(res, fp) + } + + // If the URL matches one of these, it's an event stream + if (req.url.startsWith("/posts.html")) return servePosts(req, res) + if (req.url === "/comments.html") return makeStream(req, res, DATA.comments, formatComment) + if (req.url === "/albums.html") return makeStream(req, res, DATA.albums, formatAlbum) + if (req.url === "/todos.html") return makeStream(req, res, DATA.todos, formatTodo) + if (req.url === "/users.html") return makeStream(req, res, DATA.users, formatUser) + + // Randomly-generated HTML + if (req.url === "/page/random") return serveRandomHtml(req, res) + + // Otherwise, attempt to serve the file from ./static and return a 404 on failure + try { + await serveFileFromStatic(req, res) + } catch (error) { + sendNotFound(res) + } +} + +// Attach the websockets +server.on('upgrade', (request, socket, head) => { + if (request.url === '/echo') ECHO_WS.handle(request, socket, head) + if (request.url === '/heartbeat') HEARTBEAT_WS.handle(request, socket, head) +}) + +// Start listening +server.listen(PORT, HOSTNAME, () => { + console.log('Loading the WebSocket / Server-Side Event Tests...'); + console.log(`You can run them at http://${HOSTNAME}:${PORT}/`); +}) + +function createWebSocket (connectionFunc, closeFunc) { + const server = new WebSocketServer({ noServer: true }) + server.on('connection', connectionFunc) + if (closeFunc) server.on('close', closeFunc) + + const handle = (request, socket, head) => { + server.handleUpgrade(request, socket, head, (ws) => { + server.emit('connection', ws, request) + }) + } + return { handle } +} + +async function serveFileFromStatic (req, res) { + // For the root, serve the static index.html file + const resource = req.url === '/' ? '/index.html' : req.url + + let fp = path.join('./static/', resource) + let lstat = await fs.lstat(fp) + + // If it's a directory, re-set the fp to be the index.html of that directory + if (lstat.isDirectory()) { + fp = path.join(fp, 'index.html') + lstat = await fs.lstat(fp) + } + + if (!lstat.isFile) return sendNotFound(res) + + const withBase = fp.endsWith('.html') + return serveFile(res, fp, withBase) +} + +async function serveFile (res, fp, withBase) { + try { + const file = await fs.readFile(fp) + let text = file.toString() + if (withBase) text = SITE_BASE + text + res.end(text) + } catch (error) { + console.error(error) + sendNotFound(res) + } +} + +function servePosts (req, res) { + // Why do we have to specify a fake protocol here? Because WHATWG doesn't support relative URLs + // Maddening discussion here: https://github.com/whatwg/url/issues/531 + const url = new URL(req.url, "thismessage:/") + const types = url.searchParams?.get('types') + + const numEvents = types ? types.split(',').length : 0 + makeStream(req, res, DATA.posts, formatPost, numEvents) +} + +function sendNotFound(res) { + res.statusCode = 404 + res.setHeader('Content-Type', 'text/plain') + res.end('404 NOT FOUND') +} + +function makeStream(req, res, arr, formatFunc, numEvents = 0) { + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + Connection: 'keep-alive', + 'Cache-Control': 'no-cache' + }) + + // Make the intervals somewhat random, between 200 and 400ms + // We have some tests that create multiple streams at once, so this ensures they're all visibile + const intervalLength = Math.floor(Math.random() * 200) + 200 + + let i = 0 + const interval = setInterval(() => { + if (i == arr.length) i = 0 + + const item = arr[i] + try { + const evenNum = Math.floor(Math.random() * numEvents) + 1 + const eventName = numEvents > 0 ? `Event${evenNum}` : '(none)' + item.event = eventName + + const formattedData = formatFunc(item).replace(/\n/g, ' ') + const event = `${numEvents > 0 ? `event: ${eventName}\n` : ''}data: ${formattedData}\n\n` + res.write(event) + i++ + } catch (error) { + // Stop the interval if it errors for any reason + clearInterval(interval) + } + }, intervalLength) + + req.on('close', () => { + res.end('OK') + clearInterval(interval) + }) +} + +function serveRandomHtml(_req, res) { + const page_num = Math.trunc(Math.random() * 10**10) + const html_num = Math.trunc(Math.random() * 10**10) + const html = ` +
+ This is page ${page_num} +

+ Randomly generated HTML ${html_num} +

+ I wish I were a haiku. +
+ ` + res.end(html) +} + +function formatPost (post) { + return ` +
+
Post: ${post.title}
+
${post.body}
+
id: ${post.id}
+
user: ${post.userId}
+
event: ${post.event}
+
+ ` +} + +function formatComment (comment) { + return ` +
+
Comment: ${comment.name}
+
${comment.email}
+
id: ${comment.body}
+
event: ${comment.event}
+
+ ` +} + +function formatAlbum (album) { + return ` +
+
Album: ${album.title}
+
id: ${album.id}
+
event: ${album.event}
+
+ ` +} + +function formatTodo (todo) { + return ` +
+
To-Do: ${todo.title}
+
complete? ${todo.completed}
+
event: ${todo.event}
+
+ ` +} + +function formatUser (user) { + return ` +
+
User: ${user.name}
+
${user.email}
+
${user.address.street} ${user.address.suite}
${user.address.city}, ${user.address.zipcode}
+
event: ${user.event}
+
+ ` +} + diff --git a/www/static/test/realtime/static/black_transparent.svg b/www/static/test/ws-sse/static/black_transparent.svg similarity index 100% rename from www/static/test/realtime/static/black_transparent.svg rename to www/static/test/ws-sse/static/black_transparent.svg diff --git a/www/static/test/realtime/static/data.json b/www/static/test/ws-sse/static/data.json similarity index 100% rename from www/static/test/realtime/static/data.json rename to www/static/test/ws-sse/static/data.json diff --git a/www/static/test/ws-sse/static/index.html b/www/static/test/ws-sse/static/index.html new file mode 100644 index 00000000..6d8fc754 --- /dev/null +++ b/www/static/test/ws-sse/static/index.html @@ -0,0 +1,12 @@ +
+

Realtime Test Server

+ +

New Extensions

+

As of version 1.7, we have created two new extensions ws.js and sse.js to support realtime development in htmx. All new effort on WebSockets and Server Sent Events will occur in these extensions.

+ +

Old Tags Deprecated

+

The existing hx-ws and hx-sse tags have been deprecated and will not receive any more updates. We plan to remove these two tags from the core library in htmx version 2.0.

+ +

Try It For Yourself

+

Because extensions use a different calling syntax, there are minor differences in the way that this new code is invoked. This test server includes several demos / manual tests for each extension that you can try out for yourself. Each is presented side-by-side with test cases for the original code so that you can see the difference.

+
diff --git a/www/static/test/ws-sse/static/site-base.html b/www/static/test/ws-sse/static/site-base.html new file mode 100644 index 00000000..715734ad --- /dev/null +++ b/www/static/test/ws-sse/static/site-base.html @@ -0,0 +1,37 @@ + + +</> htmx Realtime Test Server + + + + + + + + diff --git a/www/static/test/realtime/static/sse-about.html b/www/static/test/ws-sse/static/sse-about.html similarity index 98% rename from www/static/test/realtime/static/sse-about.html rename to www/static/test/ws-sse/static/sse-about.html index 5c2c4094..f9d62d86 100644 --- a/www/static/test/realtime/static/sse-about.html +++ b/www/static/test/ws-sse/static/sse-about.html @@ -1,3 +1,4 @@ +

Server Sent Events (SSE)

SSE create a lightweight, uni-directional connection from your server to a client's web browser. They are often easier to manage than WebSockets, and are built on top ofHTTP connections (making them less likely to be blocked by firewalls).

@@ -32,3 +33,4 @@
  • MDN Web Docs
  • Can I Use?
  • +
    diff --git a/www/static/test/realtime/static/sse-multichannel-ext.html b/www/static/test/ws-sse/static/sse-multichannel-ext.html similarity index 78% rename from www/static/test/realtime/static/sse-multichannel-ext.html rename to www/static/test/ws-sse/static/sse-multichannel-ext.html index 77a28660..d40c26a3 100644 --- a/www/static/test/realtime/static/sse-multichannel-ext.html +++ b/www/static/test/ws-sse/static/sse-multichannel-ext.html @@ -1,8 +1,9 @@ +

    Multi-Channel Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    @@ -16,10 +17,11 @@ <div sse-swap="Event1">Waiting for Posts in Event1 channel...</div> </div> -
    +

    Test Cases

    Waiting for Posts in Event1 channel...
    Waiting for Posts in Event2 channel...
    Waiting for Posts in Event3 channel...
    Waiting for Posts in Event4 channel...
    -
    \ No newline at end of file +
    +
    diff --git a/www/static/test/realtime/static/sse-multichannel.html b/www/static/test/ws-sse/static/sse-multichannel.html similarity index 79% rename from www/static/test/realtime/static/sse-multichannel.html rename to www/static/test/ws-sse/static/sse-multichannel.html index 41c916ce..2ab81328 100644 --- a/www/static/test/realtime/static/sse-multichannel.html +++ b/www/static/test/ws-sse/static/sse-multichannel.html @@ -1,8 +1,9 @@ +

    Multi-Channel Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    @@ -16,10 +17,11 @@ <div hx-sse="swap:Event1">Waiting for Posts in Event1 channel...</div> </div> -
    +

    Test Cases

    Waiting for Posts in Event1 channel...
    Waiting for Posts in Event2 channel...
    Waiting for Posts in Event3 channel...
    Waiting for Posts in Event4 channel...
    -
    \ No newline at end of file +
    +
    diff --git a/www/static/test/realtime/static/sse-multiple-ext.html b/www/static/test/ws-sse/static/sse-multiple-ext.html similarity index 55% rename from www/static/test/realtime/static/sse-multiple-ext.html rename to www/static/test/ws-sse/static/sse-multiple-ext.html index 09e47268..9f063cc0 100644 --- a/www/static/test/realtime/static/sse-multiple-ext.html +++ b/www/static/test/ws-sse/static/sse-multiple-ext.html @@ -1,13 +1,14 @@ +

    Multiple Events Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    - This page connects to a single Server Sent Event (SSE) streams, but listens to multiple events. + This page connects to a single Server Sent Event (SSE) streams, but listens to multiple events.

    Example HTML

    @@ -17,6 +18,7 @@
     

    Test Cases

    -
    Waiting for Posts...
    +
    Waiting for Posts...
    - \ No newline at end of file + +
    diff --git a/www/static/test/realtime/static/sse-multiple.html b/www/static/test/ws-sse/static/sse-multiple.html similarity index 55% rename from www/static/test/realtime/static/sse-multiple.html rename to www/static/test/ws-sse/static/sse-multiple.html index 9411d2d2..e23baec8 100644 --- a/www/static/test/realtime/static/sse-multiple.html +++ b/www/static/test/ws-sse/static/sse-multiple.html @@ -1,13 +1,14 @@ +

    Multiple Events Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    - This page connects to a single Server Sent Event (SSE) streams, but listens to multiple events. + This page connects to a single Server Sent Event (SSE) streams, but listens to multiple events.

    Example HTML

    @@ -17,6 +18,7 @@
     

    Test Cases

    -
    Waiting for Posts...
    +
    Waiting for Posts...
    - \ No newline at end of file + +
    diff --git a/www/static/test/realtime/static/sse-settle-ext.html b/www/static/test/ws-sse/static/sse-settle-ext.html similarity index 83% rename from www/static/test/realtime/static/sse-settle-ext.html rename to www/static/test/ws-sse/static/sse-settle-ext.html index 588dc866..9c9bc23e 100644 --- a/www/static/test/realtime/static/sse-settle-ext.html +++ b/www/static/test/ws-sse/static/sse-settle-ext.html @@ -1,13 +1,14 @@ +

    Settling Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    - This page connects to a single different Server Sent Event (SSE) stream. + This page connects to a single different Server Sent Event (SSE) stream. Multiple containers all listen for the same default "message" event name, but using different values for hx-swap.

    Example HTML

    @@ -18,7 +19,7 @@

    Test Cases

    -
    +
    Waiting for Comments...
    Waiting for Comments...
    Waiting for Comments...
    @@ -26,3 +27,4 @@
    Waiting for Comments...
    +
    diff --git a/www/static/test/realtime/static/sse-settle.html b/www/static/test/ws-sse/static/sse-settle.html similarity index 84% rename from www/static/test/realtime/static/sse-settle.html rename to www/static/test/ws-sse/static/sse-settle.html index 08426b36..3f35aca4 100644 --- a/www/static/test/realtime/static/sse-settle.html +++ b/www/static/test/ws-sse/static/sse-settle.html @@ -1,13 +1,14 @@ +

    Settling Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    - This page connects to a single different Server Sent Event (SSE) stream. + This page connects to a single different Server Sent Event (SSE) stream. Multiple containers all listen for the same default "message" event name, but using different values for hx-swap.

    Example HTML

    @@ -18,7 +19,7 @@

    Test Cases

    -
    +
    Waiting for Comments...
    Waiting for Comments...
    Waiting for Comments...
    @@ -26,3 +27,4 @@
    Waiting for Comments...
    +
    diff --git a/www/static/test/ws-sse/static/sse-simple-ext.html b/www/static/test/ws-sse/static/sse-simple-ext.html new file mode 100644 index 00000000..fbc800ee --- /dev/null +++ b/www/static/test/ws-sse/static/sse-simple-ext.html @@ -0,0 +1,28 @@ +
    +

    Simple Test

    + +
    + Legacy Style + New Style +
    + +

    Description

    +

    + This page connects to several different Server Sent Event (SSE) streams, listening on the default event name "message". + Each stream should populate its own container. +

    +

    Example HTML

    +
    +<div hx-ext="sse" sse-connect="http://localhost/posts.html" sse-swap="message">Waiting for Posts...</div>
    +
    + +

    Test Cases

    + +
    +
    Waiting for Posts...
    +
    Waiting for Comments...
    +
    Waiting for Albums...
    +
    Waiting for ToDos...
    +
    Waiting for Users...
    +
    +
    diff --git a/www/static/test/ws-sse/static/sse-simple.html b/www/static/test/ws-sse/static/sse-simple.html new file mode 100644 index 00000000..e6b38e61 --- /dev/null +++ b/www/static/test/ws-sse/static/sse-simple.html @@ -0,0 +1,28 @@ +
    +

    Simple Test

    + +
    + Legacy Style + New Style +
    + +

    Description

    +

    + This page connects to several different Server Sent Event (SSE) streams, listening on the default event name "message". + Each stream should populate its own container. +

    +

    Example HTML

    +
    +<div hx-sse="connect:http://localhost/posts.html swap:message">Waiting for Posts...</div>
    +
    + +

    Test Cases

    + +
    +
    Waiting for Posts...
    +
    Waiting for Comments...
    +
    Waiting for Albums...
    +
    Waiting for ToDos...
    +
    Waiting for Users...
    +
    +
    diff --git a/www/static/test/realtime/static/sse-target-ext.html b/www/static/test/ws-sse/static/sse-target-ext.html similarity index 87% rename from www/static/test/realtime/static/sse-target-ext.html rename to www/static/test/ws-sse/static/sse-target-ext.html index 55c531b1..2cf52461 100644 --- a/www/static/test/realtime/static/sse-target-ext.html +++ b/www/static/test/ws-sse/static/sse-target-ext.html @@ -1,13 +1,14 @@ +

    Event Target Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    - This page connects to several different different Server Sent Event (SSE) stream. + This page connects to several different different Server Sent Event (SSE) stream. Contents from each stream are sent to a single DOM element.

    Example HTML

    @@ -27,3 +28,4 @@
    Waiting for records from any of: Posts/Comments/Albums/ToDos/Users
    +
    diff --git a/www/static/test/realtime/static/sse-target.html b/www/static/test/ws-sse/static/sse-target.html similarity index 63% rename from www/static/test/realtime/static/sse-target.html rename to www/static/test/ws-sse/static/sse-target.html index b014c5be..9435e87e 100644 --- a/www/static/test/realtime/static/sse-target.html +++ b/www/static/test/ws-sse/static/sse-target.html @@ -1,12 +1,14 @@ +

    Event Target Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    The original implementation of hx-sse does not use for hx-target attributes. Therefore, this test is not implemented on this page. -

    \ No newline at end of file +

    +
    diff --git a/www/static/test/ws-sse/static/sse-triggers-ext.html b/www/static/test/ws-sse/static/sse-triggers-ext.html new file mode 100644 index 00000000..37bca66d --- /dev/null +++ b/www/static/test/ws-sse/static/sse-triggers-ext.html @@ -0,0 +1,28 @@ +
    +

    Event Trigger Test

    + +
    + Legacy Style + New Style +
    + +

    Description

    +

    + This page connects to a single different Server Sent Event (SSE) streams, listening on events named "Event1", "Event2", "Event3", and "Event4". + Each event is used as a trigger for hx-get to load a random page from the server. +

    +

    Example HTML

    +
    +<div hx-ext="sse" sse-connect="http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
    +	<div hx-get="http://localhost/page/random" hx-trigger="sse:Event1">Waiting for Posts...</div>
    +</div>
    +
    + +
    +

    Test Cases

    +
    Waiting for Posts in Event1 channel...
    +
    Waiting for Posts in Event2 channel...
    +
    Waiting for Posts in Event3 channel...
    +
    Waiting for Posts in Event4 channel...
    +
    +
    diff --git a/www/static/test/ws-sse/static/sse-triggers.html b/www/static/test/ws-sse/static/sse-triggers.html new file mode 100644 index 00000000..60b2eb28 --- /dev/null +++ b/www/static/test/ws-sse/static/sse-triggers.html @@ -0,0 +1,28 @@ +
    +

    Event Trigger Test

    + +
    + Legacy Style + New Style +
    + +

    Description

    +

    + This page connects to a single different Server Sent Event (SSE) streams, listening on events named "Event1", "Event2", "Event3", and "Event4". + Each event is used as a trigger for hx-get to load a random page from the server. +

    +

    Example HTML

    +
    +<div hx-sse="connect:http://localhost/posts.html?types=Event1%2cEvent2%2cEvent3%2cEvent4">
    +	<div hx-get="http://localhost/page/random" hx-trigger="sse:Event1">Waiting for Posts...</div>
    +</div>
    +
    + +
    +

    Test Cases

    +
    Waiting for Posts in Event1 channel...
    +
    Waiting for Posts in Event2 channel...
    +
    Waiting for Posts in Event3 channel...
    +
    Waiting for Posts in Event4 channel...
    +
    +
    diff --git a/www/static/test/realtime/static/stylesheet.css b/www/static/test/ws-sse/static/stylesheet.css similarity index 98% rename from www/static/test/realtime/static/stylesheet.css rename to www/static/test/ws-sse/static/stylesheet.css index 402d9fe5..8b689394 100644 --- a/www/static/test/realtime/static/stylesheet.css +++ b/www/static/test/ws-sse/static/stylesheet.css @@ -117,7 +117,7 @@ body { font-weight:bold; } -#page { +main { margin: 50px; padding-left:200px; } @@ -158,7 +158,7 @@ body { background-color:var(--gray05); border:solid 1px var(--gray30); border-bottom: solid 1px var(--gray40); - border-radius: 4px 4px 0px 0px; + border-radius: 4px 4px 0px 0px; color: var(--gray50); font-family: inherit; font-size:1.1rem; diff --git a/www/static/test/realtime/static/white_transparent.svg b/www/static/test/ws-sse/static/white_transparent.svg similarity index 100% rename from www/static/test/realtime/static/white_transparent.svg rename to www/static/test/ws-sse/static/white_transparent.svg diff --git a/www/static/test/realtime/static/ws-about.html b/www/static/test/ws-sse/static/ws-about.html similarity index 98% rename from www/static/test/realtime/static/ws-about.html rename to www/static/test/ws-sse/static/ws-about.html index 2da65d62..bee0f6f7 100644 --- a/www/static/test/realtime/static/ws-about.html +++ b/www/static/test/ws-sse/static/ws-about.html @@ -1,3 +1,4 @@ +

    WebSockets

    WebSockets create a fast, bi-directional connection between your server and a client's web browser.

    @@ -32,3 +33,4 @@
  • MDN Web Docs
  • Can I Use?
  • +
    diff --git a/www/static/test/realtime/static/ws-echo-ext.html b/www/static/test/ws-sse/static/ws-echo-ext.html similarity index 81% rename from www/static/test/realtime/static/ws-echo-ext.html rename to www/static/test/ws-sse/static/ws-echo-ext.html index e0b573fd..5b0640ca 100644 --- a/www/static/test/realtime/static/ws-echo-ext.html +++ b/www/static/test/ws-sse/static/ws-echo-ext.html @@ -1,8 +1,9 @@ +

    Echo Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    @@ -23,7 +24,7 @@ back to you in a separate message

    </div> -
    +

    Send a Message

    @@ -36,4 +37,5 @@ back to you in a separate message


    Receive a Message

    -
    \ No newline at end of file +
    +
    diff --git a/www/static/test/realtime/static/ws-echo.html b/www/static/test/ws-sse/static/ws-echo.html similarity index 82% rename from www/static/test/realtime/static/ws-echo.html rename to www/static/test/ws-sse/static/ws-echo.html index 9564587e..60eceb78 100644 --- a/www/static/test/realtime/static/ws-echo.html +++ b/www/static/test/ws-sse/static/ws-echo.html @@ -1,8 +1,9 @@ +

    Echo Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    @@ -23,7 +24,7 @@ back to you in a separate message

    </div> -
    +

    Send a Message

    @@ -36,4 +37,5 @@ back to you in a separate message


    Receive a Message

    -
    \ No newline at end of file +
    +
    diff --git a/www/static/test/realtime/static/ws-heartbeat-ext.html b/www/static/test/ws-sse/static/ws-heartbeat-ext.html similarity index 64% rename from www/static/test/realtime/static/ws-heartbeat-ext.html rename to www/static/test/ws-sse/static/ws-heartbeat-ext.html index d7c6af15..02ee4a79 100644 --- a/www/static/test/realtime/static/ws-heartbeat-ext.html +++ b/www/static/test/ws-sse/static/ws-heartbeat-ext.html @@ -1,8 +1,9 @@ +

    Heartbeat Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    @@ -16,8 +17,9 @@ </div> -
    +

    WebSocket Messages

    Each message just contains a random number generated by the server

    -
    Waiting...
    +
    Connecting...
    +
    diff --git a/www/static/test/realtime/static/ws-heartbeat.html b/www/static/test/ws-sse/static/ws-heartbeat.html similarity index 64% rename from www/static/test/realtime/static/ws-heartbeat.html rename to www/static/test/ws-sse/static/ws-heartbeat.html index a51e7e02..0b5eb40b 100644 --- a/www/static/test/realtime/static/ws-heartbeat.html +++ b/www/static/test/ws-sse/static/ws-heartbeat.html @@ -1,8 +1,9 @@ +

    Heartbeat Test

    - Legacy Style - New Style + Legacy Style + New Style

    Description

    @@ -16,8 +17,9 @@ </div> -
    +

    WebSocket Messages

    Each message just contains a random number generated by the server

    -
    Waiting...
    -
    \ No newline at end of file +
    Connecting...
    +
    +
    diff --git a/www/static/test/realtime/static/ws-reconnect-ext.html b/www/static/test/ws-sse/static/ws-reconnect-ext.html similarity index 100% rename from www/static/test/realtime/static/ws-reconnect-ext.html rename to www/static/test/ws-sse/static/ws-reconnect-ext.html diff --git a/www/static/test/realtime/static/ws-reconnect.html b/www/static/test/ws-sse/static/ws-reconnect.html similarity index 100% rename from www/static/test/realtime/static/ws-reconnect.html rename to www/static/test/ws-sse/static/ws-reconnect.html