diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3dfcfec --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +GO_FILES = $(shell find generate/*.json | ack '/(?P.*?)\.' --output '$$+{fname}.go') + +all: + go generate +.PHONY: all + +clean: + rm $(GO_FILES) +.PHONY: clean diff --git a/callback.go b/callback.go new file mode 100644 index 0000000..33e1bf2 --- /dev/null +++ b/callback.go @@ -0,0 +1,19 @@ +package dom + +import "syscall/js" + +type Callback struct { + js.Callback +} + +func (c *Callback) JSValue() js.Value { return c.Callback.Value } + +type EventCallbackFunction func(*Event) + +func NewEventCallback(c EventCallbackFunction) *Callback { + return &Callback{ + Callback: js.NewEventCallback(0, func(evt js.Value) { + c(NewEvent(evt)) + }), + } +} diff --git a/cmd/dom/main.go b/cmd/dom/main.go index 3d9d3c8..7c6ab6b 100644 --- a/cmd/dom/main.go +++ b/cmd/dom/main.go @@ -1,22 +1,25 @@ package main import ( - "fmt" - "syscall/js" - dom "github.com/brettlangdon/go-dom/v1" + console "github.com/brettlangdon/go-dom/v1/console" + document "github.com/brettlangdon/go-dom/v1/document" ) -func onLoad(evt js.Value) { - fmt.Println("LOADED") - fmt.Printf("%#v\r\n", evt) +func onClick(evt *dom.Event) { + elm := evt.GetTarget().ToElement() + console.Log(evt, elm) } func main() { - dom.Document.AddEventListener("DOMContentLoaded", js.NewEventCallback(0, onLoad)) - fmt.Printf("%#v\r\n", dom.Document) - body := dom.Document.GetBody() - for i := 0; i < 50; i += 1 { - body.AppendChild(dom.Document.CreateElement(fmt.Sprintf("TEST-%d", i))) + id := "app" + app := document.GetElementById(id) + if app == nil { + console.Error("Could not find element with id %s\r\n", id) + return } + + document.AddEventListener("click", dom.NewEventCallback(onClick)) + stop := make(chan int) + <-stop } diff --git a/console.go b/console.go new file mode 100644 index 0000000..725b9ec --- /dev/null +++ b/console.go @@ -0,0 +1,37 @@ +// DO NOT EDIT - generated file +package dom + +import "syscall/js" + +type Console struct { + Value +} + +func NewConsole(v js.Value) *Console { + val := Value{Value: v} + if val.IsNull() || val.IsUndefined() { + return nil + } + return val.ToConsole() +} +func (v Value) ToConsole() *Console { return &Console{Value: v} } +func (c *Console) Log(v ...interface{}) Value { + vVaridic := make([]interface{}, 0) + for _, a := range v { + vVaridic = append(vVaridic, ToJSValue(a)) + } + val := Value{Value: c.Call("log", vVaridic...)} + return val +} +func (c *Console) Error(v ...interface{}) Value { + vVaridic := make([]interface{}, 0) + for _, a := range v { + vVaridic = append(vVaridic, ToJSValue(a)) + } + val := Value{Value: c.Call("error", vVaridic...)} + return val +} +func (c *Console) Dir(v JSValue) Value { + val := Value{Value: c.Call("dir", ToJSValue(v))} + return val +} diff --git a/console/console.go b/console/console.go new file mode 100644 index 0000000..d194c3a --- /dev/null +++ b/console/console.go @@ -0,0 +1,20 @@ +// DO NOT EDIT - generated file +package console + +import "syscall/js" +import dom "github.com/brettlangdon/go-dom/v1" + +var c *dom.Console + +func init() { + c = dom.NewConsole(js.Global().Get("console")) +} +func Log(v ...interface{}) dom.Value { + return c.Log(v...) +} +func Error(v ...interface{}) dom.Value { + return c.Error(v...) +} +func Dir(v dom.JSValue) dom.Value { + return c.Dir(v) +} diff --git a/document.go b/document.go index efefb89..9cdadd7 100644 --- a/document.go +++ b/document.go @@ -3,107 +3,116 @@ package dom import "syscall/js" -type document struct { - js.Value +type Document struct { + Value } -func (d *document) JSValue() js.Value { return d.Value } -func (d *document) GetBody() *Element { - val := d.Get("body") - return &Element{Value: val} +func NewDocument(v js.Value) *Document { + val := Value{Value: v} + if val.IsNull() || val.IsUndefined() { + return nil + } + return val.ToDocument() +} +func (v Value) ToDocument() *Document { return &Document{Value: v} } +func (d *Document) GetBody() *Element { + val := Value{Value: d.Get("body")} + return NewElement(val.JSValue()) } -func (d *document) CreateElement(tagName string) *Element { - val := d.Call("createElement", ToJSValue(tagName)) - return &Element{Value: val} +func (d *Document) CreateElement(tagName string) *Element { + val := Value{Value: d.Call("createElement", ToJSValue(tagName))} + return NewElement(val.JSValue()) } -func (d *document) GetElementById(id string) *Element { - val := d.Call("getElementById", ToJSValue(id)) - return &Element{Value: val} +func (d *Document) GetElementById(id string) *Element { + val := Value{Value: d.Call("getElementById", ToJSValue(id))} + return NewElement(val.JSValue()) } -func (d *document) GetElementsByName(name string) []*Element { - val := d.Call("getElementsByName", ToJSValue(name)) +func (d *Document) GetElementsByName(name string) []*Element { + val := Value{Value: d.Call("getElementsByName", ToJSValue(name))} elms := make([]*Element, 0) for i := 0; i < val.Length(); i += 1 { - elms = append(elms, &Element{Value: val.Index(i)}) + elms = append(elms, NewElement(val.Index(i))) } return elms } -func (d *document) Write(markup string) { - d.Call("write", ToJSValue(markup)) +func (d *Document) Write(markup string) Value { + val := Value{Value: d.Call("write", ToJSValue(markup))} + return val } -func (d *document) AddEventListener(t string, listener js.Callback) { - d.Call("addEventListener", ToJSValue(t), ToJSValue(listener)) +func (d *Document) AddEventListener(t string, listener *Callback) Value { + val := Value{Value: d.Call("addEventListener", ToJSValue(t), ToJSValue(listener))} + return val } -func (d *document) AppendChild(aChild *Element) *Element { - val := d.Call("appendChild", ToJSValue(aChild)) - return &Element{Value: val} +func (d *Document) AppendChild(aChild *Element) *Element { + val := Value{Value: d.Call("appendChild", ToJSValue(aChild))} + return NewElement(val.JSValue()) } -func (d *document) GetBaseURI() string { - val := d.Get("baseURI") +func (d *Document) GetBaseURI() string { + val := Value{Value: d.Get("baseURI")} return val.String() } -func (d *document) GetFirstChild() *Element { - val := d.Get("firstChild") - return &Element{Value: val} +func (d *Document) GetFirstChild() *Element { + val := Value{Value: d.Get("firstChild")} + return NewElement(val.JSValue()) } -func (d *document) GetLastChild() *Element { - val := d.Get("lastChild") - return &Element{Value: val} +func (d *Document) GetLastChild() *Element { + val := Value{Value: d.Get("lastChild")} + return NewElement(val.JSValue()) } -func (d *document) GetNextSibling() *Element { - val := d.Get("nextSibling") - return &Element{Value: val} +func (d *Document) GetNextSibling() *Element { + val := Value{Value: d.Get("nextSibling")} + return NewElement(val.JSValue()) } -func (d *document) GetPreviousSibling() *Element { - val := d.Get("previousSibling") - return &Element{Value: val} +func (d *Document) GetPreviousSibling() *Element { + val := Value{Value: d.Get("previousSibling")} + return NewElement(val.JSValue()) } -func (d *document) GetParentElement() *Element { - val := d.Get("parentElement") - return &Element{Value: val} +func (d *Document) GetParentElement() *Element { + val := Value{Value: d.Get("parentElement")} + return NewElement(val.JSValue()) } -func (d *document) GetRootElement() *Element { - val := d.Get("rootElement") - return &Element{Value: val} +func (d *Document) GetRootElement() *Element { + val := Value{Value: d.Get("rootElement")} + return NewElement(val.JSValue()) } -func (d *document) GetPrefix() string { - val := d.Get("prefix") +func (d *Document) GetPrefix() string { + val := Value{Value: d.Get("prefix")} return val.String() } -func (d *document) GetNodeName() string { - val := d.Get("nodeName") +func (d *Document) GetNodeName() string { + val := Value{Value: d.Get("nodeName")} return val.String() } -func (d *document) GetTextContent() string { - val := d.Get("textContent") +func (d *Document) GetTextContent() string { + val := Value{Value: d.Get("textContent")} return val.String() } -func (d *document) SetTextContent(v string) { +func (d *Document) SetTextContent(v string) { d.Set("textContent", v) } -func (d *document) QuerySelector(selector string) *Element { - val := d.Call("querySelector", ToJSValue(selector)) - return &Element{Value: val} +func (d *Document) QuerySelector(selector string) *Element { + val := Value{Value: d.Call("querySelector", ToJSValue(selector))} + return NewElement(val.JSValue()) } -func (d *document) QuerySelectorAll(selector string) []*Element { - val := d.Call("querySelectorAll", ToJSValue(selector)) +func (d *Document) QuerySelectorAll(selector string) []*Element { + val := Value{Value: d.Call("querySelectorAll", ToJSValue(selector))} elms := make([]*Element, 0) for i := 0; i < val.Length(); i += 1 { - elms = append(elms, &Element{Value: val.Index(i)}) + elms = append(elms, NewElement(val.Index(i))) } return elms } -func (d *document) GetClassName() string { - val := d.Get("className") +func (d *Document) GetClassName() string { + val := Value{Value: d.Get("className")} return val.String() } -func (d *document) SetClassName(v string) { +func (d *Document) SetClassName(v string) { d.Set("className", v) } -func (d *document) GetId() string { - val := d.Get("id") +func (d *Document) GetId() string { + val := Value{Value: d.Get("id")} return val.String() } -func (d *document) SetId(v string) { +func (d *Document) SetId(v string) { d.Set("id", v) } diff --git a/document/document.go b/document/document.go new file mode 100644 index 0000000..9331e35 --- /dev/null +++ b/document/document.go @@ -0,0 +1,83 @@ +// DO NOT EDIT - generated file +package document + +import "syscall/js" +import dom "github.com/brettlangdon/go-dom/v1" + +var d *dom.Document + +func init() { + d = dom.NewDocument(js.Global().Get("document")) +} +func GetBody() *dom.Element { + return d.GetBody() +} +func CreateElement(tagName string) *dom.Element { + return d.CreateElement(tagName) +} +func GetElementById(id string) *dom.Element { + return d.GetElementById(id) +} +func GetElementsByName(name string) []*dom.Element { + return d.GetElementsByName(name) +} +func Write(markup string) dom.Value { + return d.Write(markup) +} +func AddEventListener(t string, listener *dom.Callback) dom.Value { + return d.AddEventListener(t, listener) +} +func AppendChild(aChild *dom.Element) *dom.Element { + return d.AppendChild(aChild) +} +func GetBaseURI() string { + return d.GetBaseURI() +} +func GetFirstChild() *dom.Element { + return d.GetFirstChild() +} +func GetLastChild() *dom.Element { + return d.GetLastChild() +} +func GetNextSibling() *dom.Element { + return d.GetNextSibling() +} +func GetPreviousSibling() *dom.Element { + return d.GetPreviousSibling() +} +func GetParentElement() *dom.Element { + return d.GetParentElement() +} +func GetRootElement() *dom.Element { + return d.GetRootElement() +} +func GetPrefix() string { + return d.GetPrefix() +} +func GetNodeName() string { + return d.GetNodeName() +} +func GetTextContent() string { + return d.GetTextContent() +} +func SetTextContent(v string) { + d.SetTextContent(v) +} +func QuerySelector(selector string) *dom.Element { + return d.QuerySelector(selector) +} +func QuerySelectorAll(selector string) []*dom.Element { + return d.QuerySelectorAll(selector) +} +func GetClassName() string { + return d.GetClassName() +} +func SetClassName(v string) { + d.SetClassName(v) +} +func GetId() string { + return d.GetId() +} +func SetId(v string) { + d.SetId(v) +} diff --git a/element.go b/element.go index 6cfb2a9..d6a3a33 100644 --- a/element.go +++ b/element.go @@ -4,81 +4,89 @@ package dom import "syscall/js" type Element struct { - js.Value + Value } -func (e *Element) JSValue() js.Value { return e.Value } -func (e *Element) AddEventListener(t string, listener js.Callback) { - e.Call("addEventListener", ToJSValue(t), ToJSValue(listener)) +func NewElement(v js.Value) *Element { + val := Value{Value: v} + if val.IsNull() || val.IsUndefined() { + return nil + } + return val.ToElement() +} +func (v Value) ToElement() *Element { return &Element{Value: v} } +func (e *Element) AddEventListener(t string, listener *Callback) Value { + val := Value{Value: e.Call("addEventListener", ToJSValue(t), ToJSValue(listener))} + return val } func (e *Element) AppendChild(aChild *Element) *Element { - val := e.Call("appendChild", ToJSValue(aChild)) - return &Element{Value: val} + val := Value{Value: e.Call("appendChild", ToJSValue(aChild))} + return NewElement(val.JSValue()) } func (e *Element) GetBaseURI() string { - val := e.Get("baseURI") + val := Value{Value: e.Get("baseURI")} return val.String() } func (e *Element) GetFirstChild() *Element { - val := e.Get("firstChild") - return &Element{Value: val} + val := Value{Value: e.Get("firstChild")} + return NewElement(val.JSValue()) } func (e *Element) GetLastChild() *Element { - val := e.Get("lastChild") - return &Element{Value: val} + val := Value{Value: e.Get("lastChild")} + return NewElement(val.JSValue()) } func (e *Element) GetNextSibling() *Element { - val := e.Get("nextSibling") - return &Element{Value: val} + val := Value{Value: e.Get("nextSibling")} + return NewElement(val.JSValue()) } func (e *Element) GetPreviousSibling() *Element { - val := e.Get("previousSibling") - return &Element{Value: val} + val := Value{Value: e.Get("previousSibling")} + return NewElement(val.JSValue()) } func (e *Element) GetParentElement() *Element { - val := e.Get("parentElement") - return &Element{Value: val} + val := Value{Value: e.Get("parentElement")} + return NewElement(val.JSValue()) } func (e *Element) GetRootElement() *Element { - val := e.Get("rootElement") - return &Element{Value: val} + val := Value{Value: e.Get("rootElement")} + return NewElement(val.JSValue()) } func (e *Element) GetPrefix() string { - val := e.Get("prefix") + val := Value{Value: e.Get("prefix")} return val.String() } func (e *Element) GetNodeName() string { - val := e.Get("nodeName") + val := Value{Value: e.Get("nodeName")} return val.String() } func (e *Element) GetTextContent() string { - val := e.Get("textContent") + val := Value{Value: e.Get("textContent")} return val.String() } func (e *Element) SetTextContent(v string) { e.Set("textContent", v) } func (e *Element) QuerySelector(selector string) *Element { - val := e.Call("querySelector", ToJSValue(selector)) - return &Element{Value: val} + val := Value{Value: e.Call("querySelector", ToJSValue(selector))} + return NewElement(val.JSValue()) } func (e *Element) QuerySelectorAll(selector string) []*Element { - val := e.Call("querySelectorAll", ToJSValue(selector)) + val := Value{Value: e.Call("querySelectorAll", ToJSValue(selector))} elms := make([]*Element, 0) for i := 0; i < val.Length(); i += 1 { - elms = append(elms, &Element{Value: val.Index(i)}) + elms = append(elms, NewElement(val.Index(i))) } return elms } func (e *Element) GetClassName() string { - val := e.Get("className") + val := Value{Value: e.Get("className")} return val.String() } func (e *Element) SetClassName(v string) { e.Set("className", v) } func (e *Element) GetId() string { - val := e.Get("id") + val := Value{Value: e.Get("id")} return val.String() } func (e *Element) SetId(v string) { diff --git a/event.go b/event.go index 57f9c9f..e18825e 100644 --- a/event.go +++ b/event.go @@ -4,32 +4,42 @@ package dom import "syscall/js" type Event struct { - js.Value + Value } -func (e *Event) JSValue() js.Value { return e.Value } -func (e *Event) PreventDefault() { - e.Call("preventDefault") +func NewEvent(v js.Value) *Event { + val := Value{Value: v} + if val.IsNull() || val.IsUndefined() { + return nil + } + return val.ToEvent() +} +func (v Value) ToEvent() *Event { return &Event{Value: v} } +func (e *Event) PreventDefault() Value { + val := Value{Value: e.Call("preventDefault")} + return val } -func (e *Event) StopPropagation() { - e.Call("stopPropagation") +func (e *Event) StopPropagation() Value { + val := Value{Value: e.Call("stopPropagation")} + return val } -func (e *Event) StopImmediatePropagation() { - e.Call("stopImmediatePropagation") +func (e *Event) StopImmediatePropagation() Value { + val := Value{Value: e.Call("stopImmediatePropagation")} + return val } -func (e *Event) GetCurrentTarget() js.Value { - val := e.Get("currentTarget") +func (e *Event) GetCurrentTarget() Value { + val := Value{Value: e.Get("currentTarget")} return val } -func (e *Event) GetTarget() js.Value { - val := e.Get("target") +func (e *Event) GetTarget() Value { + val := Value{Value: e.Get("target")} return val } func (e *Event) GetType() string { - val := e.Get("type") + val := Value{Value: e.Get("type")} return val.String() } -func (e *Event) GetSrcElement() js.Value { - val := e.Get("srcElement") +func (e *Event) GetSrcElement() Value { + val := Value{Value: e.Get("srcElement")} return val } diff --git a/eventtargetiface.go b/eventtargetiface.go index 76ea8c4..ef387e0 100644 --- a/eventtargetiface.go +++ b/eventtargetiface.go @@ -1,8 +1,6 @@ // DO NOT EDIT - generated file package dom -import "syscall/js" - type EventTargetIFace interface { - AddEventListener(t string, listener js.Callback) + AddEventListener(t string, listener *Callback) } diff --git a/generate/console.json b/generate/console.json new file mode 100644 index 0000000..e3466c9 --- /dev/null +++ b/generate/console.json @@ -0,0 +1,36 @@ +{ + "Type": "Console", + "ImportJS": true, + "GlobalAPI": "console", + "Functions": [ + { + "Name": "log", + "Arguments": [ + { + "Name": "v", + "Varidic": true, + "Type": "interface{}" + } + ] + }, + { + "Name": "error", + "Arguments": [ + { + "Name": "v", + "Varidic": true, + "Type": "interface{}" + } + ] + }, + { + "Name": "dir", + "Arguments": [ + { + "Name": "v", + "Type": "JSValue" + } + ] + } + ] +} diff --git a/generate/document.json b/generate/document.json index 63e0909..0988502 100644 --- a/generate/document.json +++ b/generate/document.json @@ -1,5 +1,7 @@ { - "Type": "document", + "Type": "Document", + "GlobalAPI": "document", + "ImportJS": true, "Implements": [ "EventTargetIFace", "NodeIFace", diff --git a/generate/element.json b/generate/element.json index cfe68e8..e83da30 100644 --- a/generate/element.json +++ b/generate/element.json @@ -1,5 +1,6 @@ { "Type": "Element", + "ImportJS": true, "Implements": [ "EventTargetIFace", "NodeIFace", diff --git a/generate/event.json b/generate/event.json index a93362a..06645f2 100644 --- a/generate/event.json +++ b/generate/event.json @@ -1,5 +1,6 @@ { "Type": "Event", + "ImportJS": true, "Implements": [ "EventIFace" ] diff --git a/generate/eventiface.json b/generate/eventiface.json index c4d7f7d..c300185 100644 --- a/generate/eventiface.json +++ b/generate/eventiface.json @@ -4,12 +4,12 @@ "Properties": [ { "Name": "currentTarget", - "Type": "js.Value", + "Type": "Value", "ReadOnly": true }, { "Name": "target", - "Type": "js.Value", + "Type": "Value", "ReadOnly": true }, { @@ -19,7 +19,7 @@ }, { "Name": "srcElement", - "Type": "js.Value", + "Type": "Value", "ReadOnly": true } ], diff --git a/generate/eventtargetiface.json b/generate/eventtargetiface.json index a91cb55..e8ff9a5 100644 --- a/generate/eventtargetiface.json +++ b/generate/eventtargetiface.json @@ -1,6 +1,5 @@ { "Type": "EventTargetIFace", - "ImportJS": true, "Interface": true, "Functions": [ { @@ -12,7 +11,7 @@ }, { "Name": "listener", - "Type": "js.Callback" + "Type": "*Callback" } ] } diff --git a/generate/main.go b/generate/main.go index 6bce522..63a23bb 100644 --- a/generate/main.go +++ b/generate/main.go @@ -27,8 +27,9 @@ func (p TypeProperty) SetterName() string { } type FunctionArgument struct { - Name string - Type string + Name string + Type string + Varidic bool } type TypeFunction struct { @@ -43,18 +44,28 @@ func (f TypeFunction) GoName() string { type TypeStructure struct { Type string + GlobalAPI string ImportJS bool Interface bool Inherits []string Implements []string Properties []TypeProperty Functions []TypeFunction + String []string } func (t TypeStructure) Filename() string { return fmt.Sprintf("%s.go", strings.ToLower(t.Type)) } +func (t TypeStructure) APIFilename() string { + return fmt.Sprintf("%s/%s.go", t.PackageName(), strings.ToLower(t.Type)) +} + +func (t TypeStructure) PackageName() string { + return strings.ToLower(t.GlobalAPI) +} + func (t TypeStructure) ShortName() string { return strings.ToLower(string(t.Type[0])) } @@ -72,20 +83,20 @@ func (t TypeStructure) writeReturnValue(out *bytes.Buffer, rt string) error { switch rt { case "string": out.WriteString("return val.String()\r\n") - case "js.Value": + case "Value": out.WriteString("return val") case "NodeIFace": - out.WriteString("return &Node{ Value: val }\r\n") + out.WriteString("return NewNode(val.JSValue())\r\n") case "*Element": - out.WriteString("return &Element{ Value: val }\r\n") + out.WriteString("return NewElement(val.JSValue())\r\n") case "[]*Element": out.WriteString("elms := make([]*Element, 0)\r\n") out.WriteString("for i := 0; i < val.Length(); i += 1 {\r\n") - out.WriteString("\telms = append(elms, &Element{Value: val.Index(i)})\r\n") + out.WriteString("\telms = append(elms, NewElement(val.Index(i)))\r\n") out.WriteString("}\r\n") out.WriteString("return elms\r\n") case "": - // skip + out.WriteString("return val") default: return fmt.Errorf("Unknown function return type %s", rt) } @@ -100,44 +111,117 @@ func (t TypeStructure) writeFunctions(out *bytes.Buffer, funcs []TypeFunction) e if i > 0 { out.WriteString(",") } - out.WriteString(fmt.Sprintf("%s %s", arg.Name, arg.Type)) + at := arg.Type + if arg.Varidic { + at = "..." + at + } + out.WriteString(fmt.Sprintf("%s %s", arg.Name, at)) + } + rt := f.ReturnType + if rt == "" { + rt = "Value" } - out.WriteString(fmt.Sprintf(") %s {\r\n", f.ReturnType)) + out.WriteString(fmt.Sprintf(") %s {\r\n", rt)) - if f.ReturnType != "" { - out.WriteString("val := ") + for _, arg := range f.Arguments { + if !arg.Varidic { + continue + } + + out.WriteString(fmt.Sprintf("%sVaridic := make([]interface{}, 0)\r\n", arg.Name)) + out.WriteString(fmt.Sprintf("for _, a := range %s {\r\n", arg.Name)) + out.WriteString(fmt.Sprintf("%sVaridic = append(%sVaridic, ToJSValue(a))\r\n", arg.Name, arg.Name)) + out.WriteString("}\r\n") } - out.WriteString(fmt.Sprintf("%s.Call(\"%s\"", t.ShortName(), f.Name)) + + out.WriteString(fmt.Sprintf("val := Value{ Value: %s.Call(\"%s\"", t.ShortName(), f.Name)) for _, arg := range f.Arguments { out.WriteString(",") - out.WriteString(fmt.Sprintf("ToJSValue(%s)", arg.Name)) + if arg.Varidic { + out.WriteString(fmt.Sprintf("%sVaridic...", arg.Name)) + } else { + out.WriteString(fmt.Sprintf("ToJSValue(%s)", arg.Name)) + } } - out.WriteString(")\r\n") + out.WriteString(")}\r\n") // Return value - t.writeReturnValue(out, f.ReturnType) + t.writeReturnValue(out, rt) out.WriteString("}\r\n") } return nil } +func (t TypeStructure) writeAPIFunctions(out *bytes.Buffer, funcs []TypeFunction) error { + for _, f := range funcs { + out.WriteString(fmt.Sprintf("func %s(", f.GoName())) + for i, arg := range f.Arguments { + if i > 0 { + out.WriteString(",") + } + rt := arg.Type + if rt == "*Element" { + rt = "*dom.Element" + } else if rt == "JSValue" { + rt = "dom.JSValue" + } else if rt == "*Callback" { + rt = "*dom.Callback" + } + + if arg.Varidic { + rt = "..." + rt + } + out.WriteString(fmt.Sprintf("%s %s", arg.Name, rt)) + } + rt := f.ReturnType + if rt == "" { + rt = "Value" + } + if rt != "string" { + if rt[0] == '*' { + rt = "*dom." + strings.TrimLeft(rt, "*") + } else if rt[0] == '[' { + rt = "[]*dom." + strings.TrimLeft(rt, "[]*") + } else { + rt = "dom." + rt + } + } + + out.WriteString(fmt.Sprintf(") %s {\r\n", rt)) + out.WriteString(fmt.Sprintf("return %s.%s(", t.ShortName(), f.GoName())) + for i, arg := range f.Arguments { + if i > 0 { + out.WriteString(",") + } + if arg.Varidic { + out.WriteString(fmt.Sprintf("%s...", arg.Name)) + } else { + out.WriteString(fmt.Sprintf("%s", arg.Name)) + } + } + out.WriteString(")}\r\n") + } + return nil +} + func (t TypeStructure) writeProperties(out *bytes.Buffer, props []TypeProperty) error { for _, p := range props { - out.WriteString(fmt.Sprintf("func (%s *%s) %s() %s {\r\n", t.ShortName(), t.Type, p.GetterName(), p.Type)) - if p.Type == "" { - return fmt.Errorf("Property %q must have a type defined", p.Name) - out.WriteString("\tval := ") + rt := p.Type + if rt == "" { + rt = "Value" } - out.WriteString(fmt.Sprintf("val := %s.Get(\"%s\")\r\n", t.ShortName(), p.Name)) + + out.WriteString(fmt.Sprintf("func (%s *%s) %s() %s {\r\n", t.ShortName(), t.Type, p.GetterName(), rt)) + out.WriteString(fmt.Sprintf("val := Value{ Value: %s.Get(\"%s\")}\r\n", t.ShortName(), p.Name)) // Return value - t.writeReturnValue(out, p.Type) + t.writeReturnValue(out, rt) out.WriteString(fmt.Sprintf("}\r\n")) if !p.ReadOnly { - out.WriteString(fmt.Sprintf("func (%s *%s) %s(v %s){\r\n", t.ShortName(), t.Type, p.SetterName(), p.Type)) + out.WriteString(fmt.Sprintf("func (%s *%s) %s(v %s){\r\n", t.ShortName(), t.Type, p.SetterName(), rt)) out.WriteString(fmt.Sprintf("\t%s.Set(\"%s\", v)\r\n", t.ShortName(), p.Name)) out.WriteString("}\r\n") } @@ -145,6 +229,35 @@ func (t TypeStructure) writeProperties(out *bytes.Buffer, props []TypeProperty) return nil } +func (t TypeStructure) writeAPIProperties(out *bytes.Buffer, props []TypeProperty) error { + for _, p := range props { + rt := p.Type + if rt == "" { + rt = "Value" + } + if rt != "string" { + if rt[0] == '*' { + rt = "*dom." + strings.TrimLeft(rt, "*") + } else if rt[0] == '[' { + rt = "[]*dom." + strings.TrimLeft(rt, "[]*") + } else { + rt = "dom." + rt + } + } + + out.WriteString(fmt.Sprintf("func %s() %s {\r\n", p.GetterName(), rt)) + out.WriteString(fmt.Sprintf("return %s.%s()", t.ShortName(), p.GetterName())) + out.WriteString(fmt.Sprintf("}\r\n")) + + if !p.ReadOnly { + out.WriteString(fmt.Sprintf("func %s(v %s){\r\n", p.SetterName(), rt)) + out.WriteString(fmt.Sprintf("%s.%s(v)", t.ShortName(), p.SetterName())) + out.WriteString("}\r\n") + } + } + return nil +} + func (t TypeStructure) generateInterface(out *bytes.Buffer, types map[string]TypeStructure) error { out.WriteString(fmt.Sprintf("type %s interface {\r\n", t.Type)) @@ -161,18 +274,25 @@ func (t TypeStructure) generateInterface(out *bytes.Buffer, types map[string]Typ func (t TypeStructure) generateStruct(out *bytes.Buffer, types map[string]TypeStructure) (err error) { out.WriteString(fmt.Sprintf("type %s struct {\r\n", t.Type)) - out.WriteString("\tjs.Value\r\n") + out.WriteString("\tValue\r\n") for _, name := range t.Inherits { out.WriteString(fmt.Sprintf("\t%s\r\n", name)) } out.WriteString("}\r\n") - // Convert to type - out.WriteString(fmt.Sprintf("func JSTo%s (v js.Value) *%s { return &%s{ Value: v }}", t.Type, t.Type, t.Type)) + // DEV: Start with upper case means it is public + if t.Type[0] == strings.ToUpper(string(t.Type[0]))[0] { + // Constructor + out.WriteString(fmt.Sprintf("func New%s(v js.Value) *%s {\r\n", t.Type, t.Type)) + out.WriteString("val := Value{ Value: v }\r\n") + out.WriteString("if val.IsNull() || val.IsUndefined() { return nil }\r\n") + out.WriteString(fmt.Sprintf("return val.To%s()\r\n", t.Type)) + out.WriteString("}\r\n") - // JSValue - out.WriteString(fmt.Sprintf("func (%s *%s) JSValue() js.Value { return %s.Value }\r\n", t.ShortName(), t.Type, t.ShortName())) + // Convert to specific type helper + out.WriteString(fmt.Sprintf("func (v Value) To%s () *%s { return &%s{ Value: v }}\r\n", t.Type, t.Type, t.Type)) + } // Properties err = t.writeProperties(out, t.Properties) @@ -214,7 +334,7 @@ func (t TypeStructure) Generate(types map[string]TypeStructure) ([]byte, error) out.WriteString("// DO NOT EDIT - generated file\r\n") out.WriteString("package dom\r\n") - if t.Interface == false || t.ImportJS == true { + if t.ImportJS { out.WriteString("import \"syscall/js\"\r\n") } @@ -231,6 +351,55 @@ func (t TypeStructure) Generate(types map[string]TypeStructure) ([]byte, error) return format.Source(out.Bytes()) } +func (t TypeStructure) GenerateAPI(types map[string]TypeStructure) ([]byte, error) { + pkg := strings.ToLower(t.GlobalAPI) + out := bytes.NewBuffer(make([]byte, 0)) + out.WriteString("// DO NOT EDIT - generated file\r\n") + out.WriteString(fmt.Sprintf("package %s\r\n", pkg)) + + if t.ImportJS { + out.WriteString("import \"syscall/js\"\r\n") + } + out.WriteString("import dom \"github.com/brettlangdon/go-dom/v1\"\r\n") + + out.WriteString(fmt.Sprintf("var %s *dom.%s\r\n", t.ShortName(), t.Type)) + out.WriteString("func init() {\r\n") + out.WriteString(fmt.Sprintf("%s = dom.New%s(js.Global().Get(\"%s\"))\r\n", t.ShortName(), t.Type, t.GlobalAPI)) + out.WriteString("}\r\n") + + err := t.writeAPIProperties(out, t.Properties) + if err != nil { + return nil, err + } + + err = t.writeAPIFunctions(out, t.Functions) + if err != nil { + return nil, err + } + + // Interfaces + for _, it := range t.Implements { + i, ok := types[it] + if !ok { + return nil, fmt.Errorf("%q cannot implement unknown type %q", t.Type, it) + } + + // Functions + err = t.writeAPIFunctions(out, i.Functions) + if err != nil { + return nil, err + } + + // Properties + err = t.writeAPIProperties(out, i.Properties) + if err != nil { + return nil, err + } + } + + return format.Source(out.Bytes()) +} + func loadFile(fname string) (t TypeStructure, err error) { fp, err := os.Open(fname) if err != nil { @@ -259,6 +428,28 @@ func processType(t TypeStructure, types map[string]TypeStructure) error { } defer out.Close() _, err = out.Write(contents) + if err != nil { + return err + } + + if t.GlobalAPI == "" { + return nil + } + + contents, err = t.GenerateAPI(types) + if err != nil { + return err + } + err = os.MkdirAll(t.PackageName(), os.ModePerm) + if err != nil { + return err + } + api, err := os.OpenFile(t.APIFilename(), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer api.Close() + _, err = api.Write(contents) return err } diff --git a/generate/node.json b/generate/node.json index b93d65b..416908d 100644 --- a/generate/node.json +++ b/generate/node.json @@ -1,5 +1,6 @@ { "Type": "Node", + "ImportJS": true, "Implements": [ "EventTargetIFace", "NodeIFace" diff --git a/generate/window.json b/generate/window.json index 891a3b8..5d21c1e 100644 --- a/generate/window.json +++ b/generate/window.json @@ -1,3 +1,5 @@ { - "Type": "window" + "Type": "Window", + "GlobalAPI": "window", + "ImportJS": true } diff --git a/init.go b/init.go index bff59f3..4759cd8 100644 --- a/init.go +++ b/init.go @@ -1,15 +1,2 @@ //go:generate go run generate/main.go package dom - -import "syscall/js" - -var ( - Document *document - Window *window -) - -func init() { - g := js.Global() - Document = &document{Value: g.Get("document")} - Window = &window{Value: g.Get("window")} -} diff --git a/node.go b/node.go index dc64170..7c8f25a 100644 --- a/node.go +++ b/node.go @@ -4,55 +4,63 @@ package dom import "syscall/js" type Node struct { - js.Value + Value } -func (n *Node) JSValue() js.Value { return n.Value } -func (n *Node) AddEventListener(t string, listener js.Callback) { - n.Call("addEventListener", ToJSValue(t), ToJSValue(listener)) +func NewNode(v js.Value) *Node { + val := Value{Value: v} + if val.IsNull() || val.IsUndefined() { + return nil + } + return val.ToNode() +} +func (v Value) ToNode() *Node { return &Node{Value: v} } +func (n *Node) AddEventListener(t string, listener *Callback) Value { + val := Value{Value: n.Call("addEventListener", ToJSValue(t), ToJSValue(listener))} + return val } func (n *Node) AppendChild(aChild *Element) *Element { - val := n.Call("appendChild", ToJSValue(aChild)) - return &Element{Value: val} + val := Value{Value: n.Call("appendChild", ToJSValue(aChild))} + return NewElement(val.JSValue()) } func (n *Node) GetBaseURI() string { - val := n.Get("baseURI") + val := Value{Value: n.Get("baseURI")} return val.String() } func (n *Node) GetFirstChild() *Element { - val := n.Get("firstChild") - return &Element{Value: val} + val := Value{Value: n.Get("firstChild")} + return NewElement(val.JSValue()) } func (n *Node) GetLastChild() *Element { - val := n.Get("lastChild") - return &Element{Value: val} + val := Value{Value: n.Get("lastChild")} + return NewElement(val.JSValue()) } func (n *Node) GetNextSibling() *Element { - val := n.Get("nextSibling") - return &Element{Value: val} + val := Value{Value: n.Get("nextSibling")} + return NewElement(val.JSValue()) } func (n *Node) GetPreviousSibling() *Element { - val := n.Get("previousSibling") - return &Element{Value: val} + val := Value{Value: n.Get("previousSibling")} + return NewElement(val.JSValue()) } func (n *Node) GetParentElement() *Element { - val := n.Get("parentElement") - return &Element{Value: val} + val := Value{Value: n.Get("parentElement")} + return NewElement(val.JSValue()) } func (n *Node) GetRootElement() *Element { - val := n.Get("rootElement") - return &Element{Value: val} + val := Value{Value: n.Get("rootElement")} + return NewElement(val.JSValue()) } func (n *Node) GetPrefix() string { - val := n.Get("prefix") + val := Value{Value: n.Get("prefix")} return val.String() } func (n *Node) GetNodeName() string { - val := n.Get("nodeName") + val := Value{Value: n.Get("nodeName")} return val.String() } func (n *Node) GetTextContent() string { - val := n.Get("textContent") + val := Value{Value: n.Get("textContent")} return val.String() } func (n *Node) SetTextContent(v string) { diff --git a/value.go b/value.go index b2679ef..cded476 100644 --- a/value.go +++ b/value.go @@ -12,3 +12,18 @@ func ToJSValue(x interface{}) js.Value { } return js.ValueOf(x) } + +type Value struct { + js.Value +} + +func (v Value) JSValue() js.Value { return v.Value } + +func (v Value) IsUndefined() bool { return v.Type() == js.TypeUndefined } +func (v Value) IsNull() bool { return v.Type() == js.TypeNull } +func (v Value) IsBoolean() bool { return v.Type() == js.TypeBoolean } +func (v Value) IsNumber() bool { return v.Type() == js.TypeNumber } +func (v Value) IsString() bool { return v.Type() == js.TypeString } +func (v Value) IsSymbol() bool { return v.Type() == js.TypeSymbol } +func (v Value) IsObject() bool { return v.Type() == js.TypeObject } +func (v Value) IsFunction() bool { return v.Type() == js.TypeFunction } diff --git a/window.go b/window.go index 97f9be2..9414380 100644 --- a/window.go +++ b/window.go @@ -3,8 +3,15 @@ package dom import "syscall/js" -type window struct { - js.Value +type Window struct { + Value } -func (w *window) JSValue() js.Value { return w.Value } +func NewWindow(v js.Value) *Window { + val := Value{Value: v} + if val.IsNull() || val.IsUndefined() { + return nil + } + return val.ToWindow() +} +func (v Value) ToWindow() *Window { return &Window{Value: v} } diff --git a/window/window.go b/window/window.go new file mode 100644 index 0000000..c33bb40 --- /dev/null +++ b/window/window.go @@ -0,0 +1,11 @@ +// DO NOT EDIT - generated file +package window + +import "syscall/js" +import dom "github.com/brettlangdon/go-dom/v1" + +var w *dom.Window + +func init() { + w = dom.NewWindow(js.Global().Get("window")) +}