Browse Source

Initial progress/prototype

master
Brett Langdon 7 years ago
commit
054e87b912
No known key found for this signature in database GPG Key ID: B664881177781B04
13 changed files with 673 additions and 0 deletions
  1. +22
    -0
      cmd/dom/main.go
  2. +94
    -0
      document.go
  3. +67
    -0
      element.go
  4. +8
    -0
      eventtarget.go
  5. +75
    -0
      generate/document.json
  6. +17
    -0
      generate/element.json
  7. +20
    -0
      generate/eventtarget.json
  8. +278
    -0
      generate/main.go
  9. +59
    -0
      generate/node.json
  10. +1
    -0
      go.mod
  11. +14
    -0
      helpers.go
  12. +12
    -0
      init.go
  13. +6
    -0
      node.go

+ 22
- 0
cmd/dom/main.go View File

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

+ 94
- 0
document.go View File

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

+ 67
- 0
element.go View File

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

+ 8
- 0
eventtarget.go View File

@ -0,0 +1,8 @@
// DO NOT EDIT - generated file
package dom
import "syscall/js"
type EventTarget interface {
AddEventListener(t string, listener js.Callback)
}

+ 75
- 0
generate/document.json View File

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

+ 17
- 0
generate/element.json View File

@ -0,0 +1,17 @@
{
"Type": "Element",
"Implements": [
"EventTarget",
"Node"
],
"Properties": [
{
"Name": "className",
"Type": "string"
},
{
"Name": "id",
"Type": "string"
}
]
}

+ 20
- 0
generate/eventtarget.json View File

@ -0,0 +1,20 @@
{
"Type": "EventTarget",
"ImportJS": true,
"Interface": true,
"Functions": [
{
"Name": "addEventListener",
"Arguments": [
{
"Name": "t",
"Type": "string"
},
{
"Name": "listener",
"Type": "js.Callback"
}
]
}
]
}

+ 278
- 0
generate/main.go View File

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

+ 59
- 0
generate/node.json View File

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

+ 1
- 0
go.mod View File

@ -0,0 +1 @@
module github.com/brettlangdon/go-dom/v1

+ 14
- 0
helpers.go View File

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

+ 12
- 0
init.go View File

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

+ 6
- 0
node.go View File

@ -0,0 +1,6 @@
// DO NOT EDIT - generated file
package dom
type Node interface {
AppendChild(aChild *Element) *Element
}

Loading…
Cancel
Save