| @ -0,0 +1,22 @@ | |||||
| package main | |||||
| import ( | |||||
| "fmt" | |||||
| "syscall/js" | |||||
| dom "github.com/brettlangdon/go-dom/v1" | |||||
| ) | |||||
| func onLoad(evt js.Value) { | |||||
| fmt.Println("LOADED") | |||||
| fmt.Printf("%#v\r\n", evt) | |||||
| } | |||||
| 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))) | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,94 @@ | |||||
| // DO NOT EDIT - generated file | |||||
| package dom | |||||
| import "syscall/js" | |||||
| type document struct { | |||||
| js.Value | |||||
| } | |||||
| func (d *document) GetBody() *Element { | |||||
| val := d.Get("body") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) CreateElement(tagName string) *Element { | |||||
| val := d.Call("createElement", ToValue(tagName)) | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetElementById(id string) *Element { | |||||
| val := d.Call("getElementById", ToValue(id)) | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) QuerySelector(selector string) *Element { | |||||
| val := d.Call("querySelector", ToValue(selector)) | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) QuerySelectorAll(selector string) []*Element { | |||||
| val := d.Call("querySelectorAll", ToValue(selector)) | |||||
| elms := make([]*Element, 0) | |||||
| for i := 0; i < val.Length(); i += 1 { | |||||
| elms = append(elms, &Element{Value: val.Index(i)}) | |||||
| } | |||||
| return elms | |||||
| } | |||||
| func (d *document) GetElementsByName(name string) []*Element { | |||||
| val := d.Call("getElementsByName", ToValue(name)) | |||||
| elms := make([]*Element, 0) | |||||
| for i := 0; i < val.Length(); i += 1 { | |||||
| elms = append(elms, &Element{Value: val.Index(i)}) | |||||
| } | |||||
| return elms | |||||
| } | |||||
| func (d *document) Write(markup string) { | |||||
| d.Call("write", ToValue(markup)) | |||||
| } | |||||
| func (d *document) AddEventListener(t string, listener js.Callback) { | |||||
| d.Call("addEventListener", ToValue(t), ToValue(listener)) | |||||
| } | |||||
| func (d *document) AppendChild(aChild *Element) *Element { | |||||
| val := d.Call("appendChild", ToValue(aChild)) | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetBaseURI() string { | |||||
| val := d.Get("baseURI") | |||||
| return val.String() | |||||
| } | |||||
| func (d *document) GetFirstChild() *Element { | |||||
| val := d.Get("firstChild") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetLastChild() *Element { | |||||
| val := d.Get("lastChild") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetNextSibling() *Element { | |||||
| val := d.Get("nextSibling") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetPreviousSibling() *Element { | |||||
| val := d.Get("previousSibling") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetParentElement() *Element { | |||||
| val := d.Get("parentElement") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetRootElement() *Element { | |||||
| val := d.Get("rootElement") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (d *document) GetPrefix() string { | |||||
| val := d.Get("prefix") | |||||
| return val.String() | |||||
| } | |||||
| func (d *document) GetNodeName() string { | |||||
| val := d.Get("nodeName") | |||||
| return val.String() | |||||
| } | |||||
| func (d *document) GetTextContent() string { | |||||
| val := d.Get("textContent") | |||||
| return val.String() | |||||
| } | |||||
| func (d *document) SetTextContent(v string) { | |||||
| d.Set("textContent", v) | |||||
| } | |||||
| @ -0,0 +1,67 @@ | |||||
| // DO NOT EDIT - generated file | |||||
| package dom | |||||
| import "syscall/js" | |||||
| type Element struct { | |||||
| js.Value | |||||
| } | |||||
| func (e *Element) GetClassName() string { | |||||
| val := e.Get("className") | |||||
| return val.String() | |||||
| } | |||||
| func (e *Element) GetId() string { | |||||
| val := e.Get("id") | |||||
| return val.String() | |||||
| } | |||||
| func (e *Element) AddEventListener(t string, listener js.Callback) { | |||||
| e.Call("addEventListener", ToValue(t), ToValue(listener)) | |||||
| } | |||||
| func (e *Element) AppendChild(aChild *Element) *Element { | |||||
| val := e.Call("appendChild", ToValue(aChild)) | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetBaseURI() string { | |||||
| val := e.Get("baseURI") | |||||
| return val.String() | |||||
| } | |||||
| func (e *Element) GetFirstChild() *Element { | |||||
| val := e.Get("firstChild") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetLastChild() *Element { | |||||
| val := e.Get("lastChild") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetNextSibling() *Element { | |||||
| val := e.Get("nextSibling") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetPreviousSibling() *Element { | |||||
| val := e.Get("previousSibling") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetParentElement() *Element { | |||||
| val := e.Get("parentElement") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetRootElement() *Element { | |||||
| val := e.Get("rootElement") | |||||
| return &Element{Value: val} | |||||
| } | |||||
| func (e *Element) GetPrefix() string { | |||||
| val := e.Get("prefix") | |||||
| return val.String() | |||||
| } | |||||
| func (e *Element) GetNodeName() string { | |||||
| val := e.Get("nodeName") | |||||
| return val.String() | |||||
| } | |||||
| func (e *Element) GetTextContent() string { | |||||
| val := e.Get("textContent") | |||||
| return val.String() | |||||
| } | |||||
| func (e *Element) SetTextContent(v string) { | |||||
| e.Set("textContent", v) | |||||
| } | |||||
| @ -0,0 +1,8 @@ | |||||
| // DO NOT EDIT - generated file | |||||
| package dom | |||||
| import "syscall/js" | |||||
| type EventTarget interface { | |||||
| AddEventListener(t string, listener js.Callback) | |||||
| } | |||||
| @ -0,0 +1,75 @@ | |||||
| { | |||||
| "Type": "document", | |||||
| "Implements": [ | |||||
| "EventTarget", | |||||
| "Node" | |||||
| ], | |||||
| "Properties": [ | |||||
| { | |||||
| "Name": "body", | |||||
| "Type": "*Element" | |||||
| } | |||||
| ], | |||||
| "Functions": [ | |||||
| { | |||||
| "Name": "createElement", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "tagName", | |||||
| "Type": "string" | |||||
| } | |||||
| ], | |||||
| "ReturnType": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "getElementById", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "id", | |||||
| "Type": "string" | |||||
| } | |||||
| ], | |||||
| "ReturnType": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "querySelector", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "selector", | |||||
| "Type": "string" | |||||
| } | |||||
| ], | |||||
| "ReturnType": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "querySelectorAll", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "selector", | |||||
| "Type": "string" | |||||
| } | |||||
| ], | |||||
| "ReturnType": "[]*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "getElementsByName", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "name", | |||||
| "Type": "string" | |||||
| } | |||||
| ], | |||||
| "ReturnType": "[]*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "write", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "markup", | |||||
| "Type": "string" | |||||
| } | |||||
| ], | |||||
| "ReturnType": null | |||||
| } | |||||
| ] | |||||
| } | |||||
| @ -0,0 +1,17 @@ | |||||
| { | |||||
| "Type": "Element", | |||||
| "Implements": [ | |||||
| "EventTarget", | |||||
| "Node" | |||||
| ], | |||||
| "Properties": [ | |||||
| { | |||||
| "Name": "className", | |||||
| "Type": "string" | |||||
| }, | |||||
| { | |||||
| "Name": "id", | |||||
| "Type": "string" | |||||
| } | |||||
| ] | |||||
| } | |||||
| @ -0,0 +1,20 @@ | |||||
| { | |||||
| "Type": "EventTarget", | |||||
| "ImportJS": true, | |||||
| "Interface": true, | |||||
| "Functions": [ | |||||
| { | |||||
| "Name": "addEventListener", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "t", | |||||
| "Type": "string" | |||||
| }, | |||||
| { | |||||
| "Name": "listener", | |||||
| "Type": "js.Callback" | |||||
| } | |||||
| ] | |||||
| } | |||||
| ] | |||||
| } | |||||
| @ -0,0 +1,278 @@ | |||||
| package main | |||||
| import ( | |||||
| "bytes" | |||||
| "encoding/json" | |||||
| "fmt" | |||||
| "go/format" | |||||
| "io/ioutil" | |||||
| "log" | |||||
| "os" | |||||
| "path/filepath" | |||||
| "strings" | |||||
| ) | |||||
| type TypeProperty struct { | |||||
| Name string | |||||
| Type string | |||||
| Setter bool | |||||
| } | |||||
| func (p TypeProperty) GetterName() string { | |||||
| return "Get" + strings.Title(p.Name) | |||||
| } | |||||
| func (p TypeProperty) SetterName() string { | |||||
| return "Set" + strings.Title(p.Name) | |||||
| } | |||||
| type FunctionArgument struct { | |||||
| Name string | |||||
| Type string | |||||
| } | |||||
| type TypeFunction struct { | |||||
| Name string | |||||
| Arguments []FunctionArgument | |||||
| ReturnType string | |||||
| } | |||||
| func (f TypeFunction) GoName() string { | |||||
| return strings.Title(f.Name) | |||||
| } | |||||
| type TypeStructure struct { | |||||
| Type string | |||||
| ImportJS bool | |||||
| Interface bool | |||||
| Inherits []string | |||||
| Implements []string | |||||
| Properties []TypeProperty | |||||
| Functions []TypeFunction | |||||
| } | |||||
| func (t TypeStructure) Filename() string { | |||||
| return fmt.Sprintf("%s.go", strings.ToLower(t.Type)) | |||||
| } | |||||
| func (t TypeStructure) ShortName() string { | |||||
| return strings.ToLower(string(t.Type[0])) | |||||
| } | |||||
| func (t TypeStructure) writeArguments(out *bytes.Buffer, args []FunctionArgument) { | |||||
| for i, arg := range args { | |||||
| if i > 0 { | |||||
| out.WriteString(",") | |||||
| } | |||||
| out.WriteString(fmt.Sprintf("%s %s", arg.Name, arg.Type)) | |||||
| } | |||||
| } | |||||
| func (t TypeStructure) writeFunctions(out *bytes.Buffer, funcs []TypeFunction) error { | |||||
| for _, f := range funcs { | |||||
| out.WriteString(fmt.Sprintf("func (%s *%s) %s(", t.ShortName(), t.Type, f.GoName())) | |||||
| for i, arg := range f.Arguments { | |||||
| if i > 0 { | |||||
| out.WriteString(",") | |||||
| } | |||||
| out.WriteString(fmt.Sprintf("%s %s", arg.Name, arg.Type)) | |||||
| } | |||||
| out.WriteString(fmt.Sprintf(") %s {\r\n", f.ReturnType)) | |||||
| if f.ReturnType != "" { | |||||
| out.WriteString("val := ") | |||||
| } | |||||
| out.WriteString(fmt.Sprintf("%s.Call(\"%s\"", t.ShortName(), f.Name)) | |||||
| for _, arg := range f.Arguments { | |||||
| out.WriteString(",") | |||||
| out.WriteString(fmt.Sprintf("ToValue(%s)", arg.Name)) | |||||
| } | |||||
| out.WriteString(")\r\n") | |||||
| switch f.ReturnType { | |||||
| case "string": | |||||
| out.WriteString("return val.String()\r\n") | |||||
| case "*Element": | |||||
| out.WriteString("return &Element{ Value: val }\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("}\r\n") | |||||
| out.WriteString("return elms\r\n") | |||||
| case "": | |||||
| // skip | |||||
| default: | |||||
| return fmt.Errorf("Unknown function return type %s", f.ReturnType) | |||||
| } | |||||
| 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)) | |||||
| out.WriteString(fmt.Sprintf("\tval := %s.Get(\"%s\")\r\n", t.ShortName(), p.Name)) | |||||
| switch p.Type { | |||||
| case "string": | |||||
| out.WriteString("return val.String()\r\n") | |||||
| case "*Element": | |||||
| out.WriteString("return &Element{ Value: val }\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("}\r\n") | |||||
| out.WriteString("return elms\r\n") | |||||
| case "": | |||||
| // skip | |||||
| default: | |||||
| return fmt.Errorf("Unknown property return type %s", p.Type) | |||||
| } | |||||
| out.WriteString(fmt.Sprintf("}\r\n")) | |||||
| if p.Setter { | |||||
| out.WriteString(fmt.Sprintf("func (%s *%s) %s(v %s){\r\n", t.ShortName(), t.Type, p.SetterName(), p.Type)) | |||||
| out.WriteString(fmt.Sprintf("\t%s.Set(\"%s\", v)\r\n", t.ShortName(), p.Name)) | |||||
| 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)) | |||||
| for _, f := range t.Functions { | |||||
| out.WriteString(fmt.Sprintf("\t%s(", f.GoName())) | |||||
| t.writeArguments(out, f.Arguments) | |||||
| out.WriteString(fmt.Sprintf(") %s\r\n", f.ReturnType)) | |||||
| } | |||||
| out.WriteString("}\r\n") | |||||
| return nil | |||||
| } | |||||
| 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") | |||||
| for _, name := range t.Inherits { | |||||
| out.WriteString(fmt.Sprintf("\t%s\r\n", name)) | |||||
| } | |||||
| out.WriteString("}\r\n") | |||||
| // Properties | |||||
| err = t.writeProperties(out, t.Properties) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| // Functions | |||||
| err = t.writeFunctions(out, t.Functions) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| // Interfaces | |||||
| for _, it := range t.Implements { | |||||
| i, ok := types[it] | |||||
| if !ok { | |||||
| return fmt.Errorf("%q cannot implement unknown type %q", t.Type, it) | |||||
| } | |||||
| // Functions | |||||
| err = t.writeFunctions(out, i.Functions) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| // Properties | |||||
| err = t.writeProperties(out, i.Properties) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| } | |||||
| return | |||||
| } | |||||
| func (t TypeStructure) Generate(types map[string]TypeStructure) ([]byte, error) { | |||||
| out := bytes.NewBuffer(make([]byte, 0)) | |||||
| out.WriteString("// DO NOT EDIT - generated file\r\n") | |||||
| out.WriteString("package dom\r\n") | |||||
| if t.Interface == false || t.ImportJS == true { | |||||
| out.WriteString("import \"syscall/js\"\r\n") | |||||
| } | |||||
| var err error | |||||
| if t.Interface { | |||||
| err = t.generateInterface(out, types) | |||||
| } else { | |||||
| err = t.generateStruct(out, types) | |||||
| } | |||||
| 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 { | |||||
| return | |||||
| } | |||||
| defer fp.Close() | |||||
| val, err := ioutil.ReadAll(fp) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| err = json.Unmarshal(val, &t) | |||||
| return | |||||
| } | |||||
| func processType(t TypeStructure, types map[string]TypeStructure) error { | |||||
| contents, err := t.Generate(types) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| out, err := os.OpenFile(t.Filename(), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| defer out.Close() | |||||
| _, err = out.Write(contents) | |||||
| return err | |||||
| } | |||||
| func main() { | |||||
| matches, err := filepath.Glob("generate/*.json") | |||||
| if err != nil { | |||||
| log.Panic(err) | |||||
| } | |||||
| types := make(map[string]TypeStructure) | |||||
| for _, fname := range matches { | |||||
| typeStructure, err := loadFile(fname) | |||||
| if err != nil { | |||||
| log.Panic(err) | |||||
| } | |||||
| types[typeStructure.Type] = typeStructure | |||||
| } | |||||
| for _, t := range types { | |||||
| log.Printf("Generating %s\r\n", t.Type) | |||||
| err = processType(t, types) | |||||
| if err != nil { | |||||
| log.Panic(err) | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,59 @@ | |||||
| { | |||||
| "Type": "Node", | |||||
| "Interface": true, | |||||
| "Properties": [ | |||||
| { | |||||
| "Name": "baseURI", | |||||
| "Type": "string" | |||||
| }, | |||||
| { | |||||
| "Name": "firstChild", | |||||
| "Type": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "lastChild", | |||||
| "Type": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "nextSibling", | |||||
| "Type": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "previousSibling", | |||||
| "Type": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "parentElement", | |||||
| "Type": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "rootElement", | |||||
| "Type": "*Element" | |||||
| }, | |||||
| { | |||||
| "Name": "prefix", | |||||
| "Type": "string" | |||||
| }, | |||||
| { | |||||
| "Name": "nodeName", | |||||
| "Type": "string" | |||||
| }, | |||||
| { | |||||
| "Name": "textContent", | |||||
| "Type": "string", | |||||
| "Setter": true | |||||
| } | |||||
| ], | |||||
| "Functions": [ | |||||
| { | |||||
| "Name": "appendChild", | |||||
| "Arguments": [ | |||||
| { | |||||
| "Name": "aChild", | |||||
| "Type": "*Element" | |||||
| } | |||||
| ], | |||||
| "ReturnType": "*Element" | |||||
| } | |||||
| ] | |||||
| } | |||||
| @ -0,0 +1 @@ | |||||
| module github.com/brettlangdon/go-dom/v1 | |||||
| @ -0,0 +1,14 @@ | |||||
| package dom | |||||
| import "syscall/js" | |||||
| func ToValue(v interface{}) js.Value { | |||||
| switch t := v.(type) { | |||||
| case *Element: | |||||
| return t.Value | |||||
| case *document: | |||||
| return t.Value | |||||
| default: | |||||
| return js.ValueOf(v) | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,12 @@ | |||||
| //go:generate go run generate/main.go | |||||
| package dom | |||||
| import "syscall/js" | |||||
| var ( | |||||
| Document *document | |||||
| ) | |||||
| func init() { | |||||
| Document = &document{Value: js.Global().Get("document")} | |||||
| } | |||||
| @ -0,0 +1,6 @@ | |||||
| // DO NOT EDIT - generated file | |||||
| package dom | |||||
| type Node interface { | |||||
| AppendChild(aChild *Element) *Element | |||||
| } | |||||