Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdata committed Oct 30, 2023
1 parent 691ead5 commit 84c3294
Show file tree
Hide file tree
Showing 17 changed files with 94 additions and 79 deletions.
46 changes: 8 additions & 38 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,57 +12,27 @@ import (

// An Element is an instance of a *Request, an UI object and a Jid.
type Element struct {
ui UI // (read-only) the UI object
jid jid.Jid // (read-only) JaWS ID, unique to this Element within it's Request
rq *Request // (read-only) the Request the Element belongs to
*Request // (read-only) the Request the Element belongs to
ui UI // (read-only) the UI object
jid jid.Jid // (read-only) JaWS ID, unique to this Element within it's Request
// internals
updating bool // about to have Update() called
wsQueue []wsMsg // changes queued
handlers []EventHandler // custom event handlers registered, if any
}

func (e *Element) String() string {
return fmt.Sprintf("Element{%T, id=%q, Tags: %v}", e.ui, e.jid, e.rq.TagsOf(e))
}

// Jaws returns the Jaws the Element belongs to.
func (e *Element) Jaws() *Jaws {
return e.rq.Jaws
}

// Request returns the Request the Element belongs to.
func (e *Element) Request() *Request {
return e.rq
}

// Session returns the Elements's Session, or nil.
func (e *Element) Session() *Session {
return e.rq.Session()
}

// Get calls Session().Get()
func (e *Element) Get(key string) (val interface{}) {
return e.Session().Get(key)
}

// Set calls Session().Get()
func (e *Element) Set(key string, val interface{}) {
e.Session().Set(key, val)
}

// Dirty calls Request().Dirty()
func (e *Element) Dirty(tags ...interface{}) {
e.rq.Dirty(tags...)
return fmt.Sprintf("Element{%T, id=%q, Tags: %v}", e.ui, e.jid, e.Request.TagsOf(e))
}

// Tag adds the given tags to the Element.
func (e *Element) Tag(tags ...interface{}) {
e.rq.Tag(e, tags...)
e.Request.Tag(e, tags...)
}

// HasTag returns true if this Element has the given tag.
func (e *Element) HasTag(tag interface{}) bool {
return e.rq.HasTag(e, tag)
return e.Request.HasTag(e, tag)
}

// Jid returns the JaWS ID for this Element, unique within it's Request.
Expand All @@ -77,7 +47,7 @@ func (e *Element) Ui() UI {

// Render calls Request.JawsRender() for this Element.
func (e *Element) Render(w io.Writer, params []interface{}) error {
return e.rq.JawsRender(e, w, params)
return e.Request.JawsRender(e, w, params)
}

func (e *Element) queue(wht what.What, data string) {
Expand All @@ -88,7 +58,7 @@ func (e *Element) queue(wht what.What, data string) {
What: wht,
})
} else {
e.rq.cancel(ErrWebsocketQueueOverflow)
e.Request.cancel(ErrWebsocketQueueOverflow)
}
}

Expand Down
4 changes: 2 additions & 2 deletions element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func TestElement_helpers(t *testing.T) {

tss := &testUi{}
e := rq.NewElement(tss)
is.Equal(e.Jaws(), rq.jw.Jaws)
is.Equal(e.Request(), rq.Request)
is.Equal(e.Jaws, rq.jw.Jaws)
is.Equal(e.Request, rq.Request)
is.Equal(e.Session(), nil)
e.Set("foo", "bar") // no session, so no effect
is.Equal(e.Get("foo"), nil)
Expand Down
3 changes: 1 addition & 2 deletions jaws.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ function jawsLost() {
innerHTML += '. Trying to reconnect.';
var elem = document.getElementById('jaws-lost');
if (elem == null) {
elem = jawsElement('<div id="jaws-lost" style="height: 3em; display: flex; justify-content: center; align-items: center; background-color: red; color: white">' +
innerHTML + '</div>');
elem = jawsElement('<div id="jaws-lost" class="jaws-alert">' + innerHTML + '</div>');
document.body.prepend(elem);
document.body.scrollTop = document.documentElement.scrollTop = 0;
} else {
Expand Down
4 changes: 3 additions & 1 deletion jawsboot/jawsboot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ func TestJawsBoot_Setup(t *testing.T) {
}

rq := jw.NewRequest(nil)
txt := string(rq.HeadHTML())
var sb strings.Builder
rq.Writer(&sb).HeadHTML()
txt := sb.String()
if !strings.Contains(txt, rq.JawsKeyString()) {
t.Error(txt)
}
Expand Down
5 changes: 5 additions & 0 deletions js.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func JawsKeyValue(jawsKey string) uint64 {
const jsLoader = `.forEach(function(c){var e=document.createElement("script");e.src=c;e.async=!1;document.head.appendChild(e);});`

// HeadHTML returns HTML code to load the given scripts and CSS files efficiently,
// as well as a basic CSS "jaws-alert" class for JaWS to use.
func HeadHTML(js []string, css []string) string {
var s []byte

Expand All @@ -67,6 +68,10 @@ func HeadHTML(js []string, css []string) string {
s = strconv.AppendQuote(s, e)
s = append(s, " as=\"script\">\n"...)
}
s = append(s, `<style>
.jaws-alert { height: 3em; display: flex; justify-content: center; align-items: center; background-color: red; color: white; }
</style>
`...)

s = append(s, "<script>["...)
for i, e := range js {
Expand Down
27 changes: 15 additions & 12 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"html"
"html/template"
"io"
"net/http"
"net/netip"
Expand Down Expand Up @@ -121,10 +120,14 @@ func (rq *Request) clearLocked() *Request {
return rq
}

// HeadHTML returns the HTML code needed to write in the HTML page's HEAD section.
func (rq *Request) HeadHTML() template.HTML {
s := rq.Jaws.headPrefix + rq.JawsKeyString() + `";</script>`
return template.HTML(s) // #nosec G203
// HeadHTML writes the HTML code needed in the HTML page's HEAD section.
func (rq *Request) HeadHTML(w io.Writer) (err error) {
if _, err = w.Write([]byte(rq.Jaws.headPrefix)); err == nil {
if _, err = w.Write([]byte(rq.JawsKeyString())); err == nil {
_, err = w.Write([]byte(`";</script><noscript><div class="jaws-alert">This site requires Javascript for full functionality.</div></noscript>`))
}
}
return
}

// GetConnectFn returns the currently set ConnectFn. That function will be called before starting the WebSocket tunnel if not nil.
Expand Down Expand Up @@ -298,9 +301,9 @@ var nextJid Jid

func (rq *Request) newElementLocked(ui UI) (elem *Element) {
elem = &Element{
jid: Jid(atomic.AddInt64((*int64)(&nextJid), 1)),
ui: ui,
rq: rq,
jid: Jid(atomic.AddInt64((*int64)(&nextJid), 1)),
ui: ui,
Request: rq,
}
rq.elems = append(rq.elems, elem)
return
Expand Down Expand Up @@ -364,8 +367,8 @@ func (rq *Request) tagExpanded(elem *Element, expandedtags []interface{}) {

// Tag adds the given tags to the given Element.
func (rq *Request) Tag(elem *Element, tags ...interface{}) {
if elem != nil && len(tags) > 0 && elem.rq == rq {
rq.tagExpanded(elem, MustTagExpand(elem.rq, tags))
if elem != nil && len(tags) > 0 && elem.Request == rq {
rq.tagExpanded(elem, MustTagExpand(elem.Request, tags))
}
}

Expand Down Expand Up @@ -635,7 +638,7 @@ func (rq *Request) sendQueue(outboundCh chan<- string, wsQueue []wsMsg) []wsMsg
}

func (rq *Request) deleteElementLocked(e *Element) {
e.rq = nil
e.Request = nil
rq.elems = slices.DeleteFunc(rq.elems, func(elem *Element) bool { return elem == e })
for k := range rq.tagMap {
rq.tagMap[k] = slices.DeleteFunc(rq.tagMap[k], func(elem *Element) bool { return elem == e })
Expand Down Expand Up @@ -695,5 +698,5 @@ func (rq *Request) onConnect() (err error) {
}

func (rq *Request) Writer(w io.Writer) RequestWriter {
return RequestWriter{Request: rq, Writer: w}
return RequestWriter{rq: rq, Writer: w}
}
4 changes: 3 additions & 1 deletion request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ func TestRequest_HeadHTML(t *testing.T) {
rq := jw.NewRequest(nil)
defer jw.recycle(rq)

txt := rq.HeadHTML()
var sb strings.Builder
rq.Writer(&sb).HeadHTML()
txt := sb.String()
is.Equal(strings.Contains(string(txt), rq.JawsKeyString()), true)
is.Equal(strings.Contains(string(txt), JavascriptPath), true)
}
Expand Down
39 changes: 36 additions & 3 deletions requestwriter.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
package jaws

import "io"
import (
"io"
"net/http"
)

type RequestWriter struct {
*Request
rq *Request
io.Writer
}

func (rw RequestWriter) UI(ui UI, params ...interface{}) error {
return rw.JawsRender(rw.NewElement(ui), rw.Writer, params)
return rw.rq.JawsRender(rw.rq.NewElement(ui), rw.Writer, params)
}

// Request returns the current jaws.Request.
func (rw RequestWriter) Request() *Request {
return rw.rq
}

// Initial returns the initial http.Request.
func (rw RequestWriter) Initial() *http.Request {
return rw.Request().Initial
}

// HeadHTML outputs the HTML code needed in the HEAD section.
func (rw RequestWriter) HeadHTML() error {
return rw.Request().HeadHTML(rw)
}

// Session returns the Requests's Session, or nil.
func (rw RequestWriter) Session() *Session {
return rw.Request().Session()
}

// Get calls Request().Get()
func (rw RequestWriter) Get(key string) (val interface{}) {
return rw.Request().Get(key)
}

// Set calls Request().Set()
func (rw RequestWriter) Set(key string, val interface{}) {
rw.Request().Set(key, val)
}
7 changes: 4 additions & 3 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ func TestSession_Use(t *testing.T) {
return
}

var sb strings.Builder
sess := jw.GetSession(r)
rq := jw.NewRequest(r)
rq := jw.NewRequest(r).Writer(&sb)
if sess != rq.Session() {
t.Error(sess)
}
Expand Down Expand Up @@ -100,7 +101,7 @@ func TestSession_Use(t *testing.T) {
rq.Set("bar", "quux")
}
w.WriteHeader(http.StatusOK)
jw.UseRequest(rq.JawsKey, r)
jw.UseRequest(rq.Request().JawsKey, r)
})

srv := httptest.NewServer(h)
Expand Down Expand Up @@ -276,7 +277,7 @@ func TestSession_Delete(t *testing.T) {
}

ts.rq.Register("byebye", func(e *Element, evt what.What, val string) error {
sess2 := ts.jw.GetSession(e.Request().Initial)
sess2 := ts.jw.GetSession(e.Request.Initial)
if x := sess2; x != ts.sess {
t.Error(x)
}
Expand Down
6 changes: 3 additions & 3 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ func (t Template) String() string {
}

func (t Template) JawsRender(e *Element, w io.Writer, params []interface{}) error {
if expandedtags, err := TagExpand(e.Request(), t.Dot); err != ErrIllegalTagType {
e.Request().tagExpanded(e, expandedtags)
if expandedtags, err := TagExpand(e.Request, t.Dot); err != ErrIllegalTagType {
e.Request.tagExpanded(e, expandedtags)
}
var sb strings.Builder
for _, s := range parseParams(e, params) {
Expand All @@ -51,7 +51,7 @@ func (t Template) JawsRender(e *Element, w io.Writer, params []interface{}) erro
attrs := template.HTMLAttr(sb.String()) // #nosec G203
return t.Execute(w, With{
Element: e,
RequestWriter: e.Request().Writer(w),
RequestWriter: e.Request.Writer(w),
Dot: t.Dot,
Attrs: attrs,
})
Expand Down
2 changes: 1 addition & 1 deletion ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (rq *Request) JawsRender(elem *Element, w io.Writer, params []interface{})
if rq.Jaws.Debug {
var sb strings.Builder
_, _ = fmt.Fprintf(&sb, "<!-- id=%q %T tags=[", elem.jid, elem.ui)
for i, tag := range elem.Request().TagsOf(elem) {
for i, tag := range elem.Request.TagsOf(elem) {
if i > 0 {
sb.WriteString(", ")
}
Expand Down
2 changes: 1 addition & 1 deletion ui_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestRequest_JawsRender_DebugOutput(t *testing.T) {
rq.Jaws.Debug = true
rq.UI(&testUi{renderFn: func(e *Element, w io.Writer, params []any) error {
e.Tag(Tag("footag"))
e.Tag(e.Request())
e.Tag(e.Request)
e.Tag(testStringer{})
return nil
}})
Expand Down
2 changes: 1 addition & 1 deletion uihtml.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type UiHtml struct {
func (ui *UiHtml) parseGetter(e *Element, getter interface{}) {
if getter != nil {
if tagger, ok := getter.(TagGetter); ok {
ui.Tag = tagger.JawsGetTag(e.Request())
ui.Tag = tagger.JawsGetTag(e.Request)
if ch, ok := getter.(ClickHandler); ok {
e.handlers = append(e.handlers, clickHandlerWapper{ch})
}
Expand Down
8 changes: 4 additions & 4 deletions uiradiogroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ type RadioElement struct {
nameAttr string
}

func (rq *Request) RadioGroup(nba *NamedBoolArray) (rel []RadioElement) {
func (rw RequestWriter) RadioGroup(nba *NamedBoolArray) (rel []RadioElement) {
nameAttr := `name="` + MakeID() + `"`
nba.ReadLocked(func(nbl []*NamedBool) {
for _, nb := range nbl {
rel = append(rel, RadioElement{
radio: rq.NewElement(NewUiRadio(nb)),
label: rq.NewElement(NewUiLabel(nb)),
radio: rw.Request().NewElement(NewUiRadio(nb)),
label: rw.Request().NewElement(NewUiLabel(nb)),
nameAttr: nameAttr,
},
)
Expand All @@ -34,7 +34,7 @@ func (re RadioElement) Radio(params ...interface{}) template.HTML {
}

// Label renders a HTML label element.
func (re *RadioElement) Label(params ...interface{}) template.HTML {
func (re RadioElement) Label(params ...interface{}) template.HTML {
var sb strings.Builder
forAttr := string(re.radio.jid.AppendQuote([]byte("for=")))
maybePanic(re.label.Render(&sb, append(params, forAttr)))
Expand Down
2 changes: 1 addition & 1 deletion uitemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ func NewUiTemplate(t Template) UiTemplate {
// The templ argument can either be a string, in which case Jaws.Template.Lookup() will
// be used to resolve it. Or it can be a *template.Template directly.
func (rq RequestWriter) Template(templ, dot interface{}, params ...interface{}) error {
return rq.UI(NewUiTemplate(rq.MakeTemplate(templ, dot)), params...)
return rq.UI(NewUiTemplate(rq.rq.MakeTemplate(templ, dot)), params...)
}
2 changes: 1 addition & 1 deletion uitemplate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestRequest_Template(t *testing.T) {
if tt.errtxt != "" {
t.Fail()
}
gotTags := elem.Request().TagsOf(elem)
gotTags := elem.Request.TagsOf(elem)
is.Equal(len(tt.tags), len(gotTags))
for _, tag := range tt.tags {
is.True(elem.HasTag(tag))
Expand Down
Loading

0 comments on commit 84c3294

Please sign in to comment.