sse.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. package engine
  2. import (
  3. "bytes"
  4. "net/http"
  5. "strings"
  6. "github.com/xyproto/algernon/utils"
  7. )
  8. // InsertAutoRefresh inserts JavaScript code to the page that makes the page
  9. // refresh itself when the source files changes.
  10. // The JavaScript depends on the event server being available.
  11. // If JavaScript can not be inserted, return the original data.
  12. // Assumes that the given htmldata is actually HTML
  13. // (looks for body/head/html tags when inserting a script tag)
  14. func (ac *Config) InsertAutoRefresh(req *http.Request, htmldata []byte) []byte {
  15. fullHost := ac.eventAddr
  16. // If the host+port starts with ":", assume it's only the port number
  17. if strings.HasPrefix(fullHost, ":") {
  18. // Add the hostname in front
  19. if ac.serverHost != "" {
  20. fullHost = ac.serverHost + ac.eventAddr
  21. } else {
  22. fullHost = utils.GetDomain(req) + ac.eventAddr
  23. }
  24. }
  25. // Wait 70% of an event duration before starting to listen for events
  26. multiplier := 0.7
  27. js := `
  28. <script>
  29. if (!!window.EventSource) {
  30. window.setTimeout(function() {
  31. var source = new EventSource(window.location.protocol + '//` + fullHost + ac.defaultEventPath + `');
  32. source.addEventListener('message', function(e) {
  33. const path = '/' + e.data;
  34. if (path.indexOf(window.location.pathname) >= 0) {
  35. location.reload()
  36. }
  37. }, false);
  38. }, ` + utils.DurationToMS(ac.refreshDuration, multiplier) + `);
  39. }
  40. </script>`
  41. // Reduce the size slightly
  42. js = strings.TrimSpace(strings.ReplaceAll(js, "\n", ""))
  43. // Remove all whitespace that is more than one space
  44. for strings.Contains(js, " ") {
  45. js = strings.ReplaceAll(js, " ", " ")
  46. }
  47. // Place the script at the end of the body, if there is a body
  48. switch {
  49. case bytes.Contains(htmldata, []byte("</body>")):
  50. return bytes.Replace(htmldata, []byte("</body>"), []byte(js+"</body>"), 1)
  51. case bytes.Contains(htmldata, []byte("<head>")):
  52. // If not, place the script in the <head>, if there is a head
  53. return bytes.Replace(htmldata, []byte("<head>"), []byte("<head>"+js), 1)
  54. case bytes.Contains(htmldata, []byte("<html>")):
  55. // If not, place the script in the <html> as a new <head>
  56. return bytes.Replace(htmldata, []byte("<html>"), []byte("<html><head>"+js+"</head>"), 1)
  57. }
  58. // In the unlikely event that no place to insert the JavaScript was found
  59. return htmldata
  60. }