| @ -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 | |||
| } | |||