func MakeWorkspace(canvas *js.Object) *Workspace { doc := js.Global.Get("document") ctx := canvas.Call("getContext", "2d") w := &Workspace{ doc: doc, canvas: canvas, ctx: ctx, x: canvas.Get("offsetLeft").Int(), y: canvas.Get("offsetTop").Int(), dx: canvas.Get("offsetWidth").Int(), dy: canvas.Get("offsetHeight").Int(), images: make(chan schema.ImageManifest), disks: make(chan string), ingresses: make(chan int), draw: make(chan struct{}), mouseDown: make(chan point), mouseMove: make(chan point), mouseUp: make(chan point), makeItSo: make(chan struct{}), cut: make(chan struct{}), } doc.Call("addEventListener", "mousedown", js.MakeFunc(w.onMouseDown), "false") doc.Call("addEventListener", "mousemove", js.MakeFunc(w.onMouseMove), "false") doc.Call("addEventListener", "mouseup", js.MakeFunc(w.onMouseUp), "false") go w.run() return w }
func main() { example := dom.GetWindow().Document().GetElementByID("example") m.Mount( example, js.M{ "view": js.MakeFunc(View), "controller": js.MakeFunc(Controller)}, ) }
func init() { prompt := js.Global.Get("godebugPrompt") if !prompt.Bool() { return } // Expose an input function to javascript for responses to prompts. input := make(chan string) js.Global.Set("godebugInput", js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} { input <- args[0].String() return nil })) // Hook up gopherJS's output function. js.Global.Set("goPrintToConsole", js.InternalObject(func(b []byte) { js.Global.Call("godebugOutput", string(b)) })) // Override our internal prompt function. promptUser = func() (response string, ok bool) { prompt.Invoke() response = <-input return response, true } }
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value { if typ.Kind() != Func { panic("reflect: call of MakeFunc with non-Func type") } t := typ.common() ftyp := (*funcType)(unsafe.Pointer(t)) fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { args := make([]Value, ftyp.NumIn()) for i := range args { argType := ftyp.In(i).common() args[i] = makeValue(argType, arguments[i], 0) } resultsSlice := fn(args) switch ftyp.NumOut() { case 0: return nil case 1: return resultsSlice[0].object() default: results := js.Global.Get("Array").New(ftyp.NumOut()) for i, r := range resultsSlice { results.SetIndex(i, r.object()) } return results } }) return Value{t, unsafe.Pointer(fv.Unsafe()), flag(Func)} }
func init() { vue.Component("js-clock", js.M{ "template": `<canvas></canvas>`, "replace": true, "ready": js.MakeFunc(func(this *js.Object, arg []*js.Object) interface{} { draw(canvas.NewElement(this.Get("$el"))) return 0 }), }) }
func wrap(v, t *js.Object) *js.Object { nt := js.Global.Get("Object").New() nt.Set("elem", t) nt.Set("kind", ptrKind) nv := js.Global.Get("Object").New() nv.Set("$val", v) nv.Set("constructor", nt) nv.Set("$get", js.MakeFunc(func(this *js.Object, _ []*js.Object) interface{} { return this.Get("$val") })) return nv }
// Bytes returns a slice of the contents of the Blob. func (b *Blob) Bytes() []byte { fileReader := js.Global.Get("FileReader").New() var wg sync.WaitGroup var buf []byte wg.Add(1) fileReader.Set("onload", js.MakeFunc(func(this *js.Object, _ []*js.Object) interface{} { defer wg.Done() buf = js.Global.Get("Uint8Array").New(this.Get("result")).Interface().([]uint8) return nil })) fileReader.Call("readAsArrayBuffer", b) wg.Wait() return buf }
func (m *Module) Controller(name string, cls interface{}) error { var err error arg, err := buildParams(cls, "ajs-service") if err != nil { println("Error building controller params: ", err.Error(), "controller: ", name) return err } m.Call("controller", name, arg) js.Global.Set(name, js.MakeFunc(makeConstructor(cls))) return nil }
func TestMakeFunc(t *testing.T) { o := js.Global.Get("Object").New() o.Set("f", js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { if this != o { t.Fail() } if len(arguments) != 2 || arguments[0].Int() != 1 || arguments[1].Int() != 2 { t.Fail() } return 3 })) if o.Call("f", 1, 2).Int() != 3 { t.Fail() } }
func makeMethodValue(op string, v Value) Value { if v.flag&flagMethod == 0 { panic("reflect: internal error: invalid use of makePartialFunc") } _, _, fn := methodReceiver(op, v, int(v.flag)>>flagMethodShift) rcvr := v.object() if isWrapped(v.typ) { rcvr = jsType(v.typ).New(rcvr) } fv := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { return js.InternalObject(fn).Call("apply", rcvr, arguments) }) return Value{v.Type().common(), unsafe.Pointer(fv.Unsafe()), v.flag&flagRO | flag(Func)} }
func buildParams(cb interface{}, name string) (js.S, error) { arg := js.S{} typ, cls := typeOf(cb) if typ.Kind() != reflect.Func { return nil, fmt.Errorf("Expected a function, got %q", typ) } i := 0 if cls { i = 1 } for ; i < typ.NumIn(); i++ { argTyp := typ.In(i) if argTyp.Kind() != reflect.Struct { argTyp = argTyp.Elem() } param, ok := argTyp.FieldByName("Object") if !ok { return nil, fmt.Errorf("Invalid paramater type %q (Missing Object)", argTyp) } if param.Tag.Get(name) == "" { if param.Tag.Get("js") == "" { return nil, fmt.Errorf("Invalid paramater type %q (Missing tag %s)", argTyp, name) } else { arg = append(arg, param.Tag.Get("js")) } } else { arg = append(arg, param.Tag.Get(name)) } } if cls { arg = append(arg, js.MakeFunc(makeConstructor(cb))) } else { arg = append(arg, cb) } return arg, nil }
func TestMakeFunc(t *testing.T) { o := js.Global.Get("Object").New() for i := 3; i < 5; i++ { x := i if i == 4 { break } o.Set("f", js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { if this != o { t.Fail() } if len(arguments) != 2 || arguments[0].Int() != 1 || arguments[1].Int() != 2 { t.Fail() } return x })) } if o.Call("f", 1, 2).Int() != 3 { t.Fail() } }
func (t *uncommonType) Method(i int) (m Method) { if t == nil || i < 0 || i >= len(t.methods) { panic("reflect: Method index out of range") } p := &t.methods[i] if p.name != nil { m.Name = *p.name } fl := flag(Func) if p.pkgPath != nil { m.PkgPath = *p.pkgPath fl |= flagStickyRO } mt := p.typ m.Type = mt prop := js.Global.Call("$methodSet", js.InternalObject(t).Get("jsType")).Index(i).Get("prop").String() fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { rcvr := arguments[0] return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) }) m.Func = Value{mt, unsafe.Pointer(fn.Unsafe()), fl} m.Index = i return }
func (t *rtype) Method(i int) (m Method) { if t.Kind() == Interface { tt := (*interfaceType)(unsafe.Pointer(t)) return tt.Method(i) } methods := t.exportedMethods() if i < 0 || i >= len(methods) { panic("reflect: Method index out of range") } p := methods[i] pname := t.nameOff(p.name) m.Name = pname.name() fl := flag(Func) mtyp := t.typeOff(p.mtyp) ft := (*funcType)(unsafe.Pointer(mtyp)) in := make([]Type, 0, 1+len(ft.in())) in = append(in, t) for _, arg := range ft.in() { in = append(in, arg) } out := make([]Type, 0, len(ft.out())) for _, ret := range ft.out() { out = append(out, ret) } mt := FuncOf(in, out, ft.IsVariadic()) m.Type = mt prop := js.Global.Call("$methodSet", js.InternalObject(t).Get("jsType")).Index(i).Get("prop").String() fn := js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { rcvr := arguments[0] return rcvr.Get(prop).Call("apply", rcvr, arguments[1:]) }) m.Func = Value{mt.(*rtype), unsafe.Pointer(fn.Unsafe()), fl} m.Index = i return m }
func main() { vue.Component("my-cpnt", js.M{ "template": "<h1>This is my testing component!</h1>" + "<content>This will only be displayed if no content is inserted</content>", "created": js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { println("'An instance of MyComponent has been created!'") this.Call("$dispatch", "msg", "hello") return 0 }), }) data := newData() datam := structs.New(data).Tag("js").Map() println("data:", data) println("datam:", datam) vm := vue.New(js.M{ "el": "#demo", // "data": js.M{ // "title": "todos", // "todos": []js.M{ // js.M{ // "done": true, // "content": "Learn JavaScript", // }, // js.M{ // "done": false, // "content": "Learn Vue.js", // }, // }, // }, "data": data, "methods": js.M{ "change": func() { // data.Title = "Funcked" }, }, "directives": js.M{ "showdone": js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { println("this.expression:", this.Get("expression")) return 0 }), }, "filters": js.M{ "testf": js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { println("testf:", this.Get("title")) return 0 }), }, "created": js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { this.Call("$on", "msg", func(msg interface{}) { println("parent got:", msg.(string)) }) return 0 }), }) // v := vue.New(vue.VueOption{ // El: "#demo", // Data: js.M{ // "title": "todos", // "todos": []js.M{ // js.M{ // "done": true, // "content": "Learn JavaScript", // }, // js.M{ // "done": false, // "content": "Learn Vue.js", // }, // }, // }, // }) println(vm.Object) println(vm.Options) vm.Watch("todos", func(newVal, oldVal *js.Object) { println(newVal.Index(0).Get("done")) }, true) // vm.On("msg", func(msg interface{}) { // println("parent got:", msg.(string)) // }) }
// Package analytics provides bindings to Google Analytics's analytics.js for // gopherjs. package analytics import "github.com/gopherjs/gopherjs/js" var ga = func() func(...interface{}) { // conversion of the code given by Google Analytics as of 2015-06-24 js.Global.Set("GoogleAnalyticsObject", "ga") if js.Global.Get("ga") == js.Undefined { js.Global.Set("ga", js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { if js.Global.Get("ga").Get("q") == js.Undefined { js.Global.Get("ga").Set("q", make(js.S, 0)) } js.Global.Get("ga").Get("q").Call("push", arguments) return js.Undefined })) } js.Global.Get("ga").Set("l", js.Global.Get("Date").New().Call("valueOf")) script := js.Global.Get("document").Call("createElement", "script") anchor := js.Global.Get("document").Call("getElementsByTagName", "script").Index(0) script.Set("async", true) script.Set("src", "//www.google-analytics.com/analytics.js") anchor.Get("parentNode").Call("insertBefore", script, anchor) return func(args ...interface{}) { js.Global.Get("ga").Invoke(args...) } }() // Create a new default tracker object. The tracking ID starts with "UA-" and
// wrapComponent creates a React class for the given Component by // creating a plain js.Object and mapping functions to it, and back. // // For example, the React `render()` method is assigned to the object. // When React calls this method, the given Component's Render() method is // called. The result of Component.Render() is recursively run through // Wrap() as well, with the final result being a series of objects given // to React that map to their respective components. // // This means that when a component calls the embedded *React methods, // such as React.SetState() end up calling the functions in this closure // which then call the normal React `this.setState()` methods. // // A lot of wiring, but the goal is that complexity in a Component is // isolated from the complexity given to React. So all of the tomfoolery // that React does to method Binding's are not actually done to the // methods of your component. // // TL;DR: The overhead caused by Wrap() is to avoid React tomfoolery // multiplying by GopherJS tomfoolery. Resulting in a (in theory) // net tomfoolery reduction. Science. // // TODO: Create the *React pipeline to communicate from the Component // to the jsObject wrapper. // // TODO: Write errors to console.error // // TODO: Drop error return. Don't think it's possible to have an error // outside of the callbacks. // // TODO: Handle the following React warning: // // Warning: Each child in an array or iterator should have a // unique "key" prop. Check the React.render call using <div>. // See https://fb.me/react-warning-keys for more information. // // Though, the responsibility of handling this may be better off on the // Component. func wrapComponent(c Component) (jsComp *js.Object, err error) { fmt.Printf("wrapComponent(<%s>)\n", c.Tag()) jsObject := js.Global.Get("Object").New() // This is a reference to the final, valid React object instance. // // React does a lot of binding/this tomfoolery, and it heavily // screws with GopherJS. As a result, custom functions won't have // valid `this` objects. // // React API functions can get a valid `this`, such as `render` and // `setInitialState`, but custom functions fail. // // Commented out, as it's likely being removed soon (no need for // the reference, Components have SetThis() //var this *js.Object // TODO: Find a way to support sane error handling. It's difficult // because the render() method is a callback from JS, and the return // value is consumed by React.js itself. Furthermore, even if we could // return an error value, we can't get access to it. render := func() *js.Object { fmt.Printf("<%s>.render\n", c.Tag()) rendComp := c.Render() var jsClass *js.Object var content string // If a component is Rendered, wrap it and return. if rendComp != nil { // If this child has Content (String), return it directly. content = rendComp.Content() if content != "" { return reactjs.CreateElement(c.Tag(), c.Props(), content) } jsClass, _ = wrapComponent(rendComp) return reactjs.CreateElement(jsClass) } // The comp did not render anything, so try to create a Tag with // it's children. // // If it has no tag, we can't do that. Error out. if c.Tag() == "" { fmt.Println("Error: Component has no Children and no Tag") return nil } children := c.Children() childrenCount := len(children) // If this comp has no children, create an empty jsComp (eg: <foo />) if childrenCount == 0 { return reactjs.CreateElement(c.Tag(), c.Props()) } // The == 1 check is done to avoid creating the slice, looping, // and (most importantly) avoid returning an array. By doing it // here, we can save a bit of overhead.. in theory. // // Premature optimization without benchmarks? heh var childComp Component if childrenCount == 1 { childComp = children[0] // If this child has Content (String), return it directly. content = childComp.Content() if content != "" { return reactjs.CreateElement(c.Tag(), c.Props(), content) } jsClass, _ = wrapComponent(childComp) return reactjs.CreateElement(c.Tag(), c.Props(), reactjs.CreateElement(jsClass)) } // A slice of this Element's Childen. Note that in the event of // a Content child, string is added to this slice instead of a // jsObject. jsChildren := make([]interface{}, childrenCount) var i int for i, childComp = range children { // If this child has Content (String), push it onto Children // directly. content = childComp.Content() if content != "" { jsChildren[i] = content continue } // Wrap the component, returning it's class jsClass, _ = wrapComponent(childComp) // And create an element from the class, adding it to the slice. jsChildren[i] = reactjs.CreateElement(jsClass) } return reactjs.CreateElement(c.Tag(), c.Props(), jsChildren) } jsObject.Set("render", render) jsObject.Set("componentDidMount", c.ComponentDidMount) jsObject.Set("componentWillUnmount", c.ComponentWillUnmount) // Temporary implementation for early testing. jsObject.Set("getInitialState", js.MakeFunc( func(reactThis *js.Object, _ []*js.Object) interface{} { if err := c.SetThis(reactThis); err != nil { fmt.Println("Error: ", err.Error()) } return nil }, )) return reactjs.CreateClass(jsObject), nil }
package main import ( "github.com/gopherjs/gopherjs/js" ) var onWordMouseOver = js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { this.Get("style").Set("color", "red") return nil }) var onWordMouseOut = js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} { this.Get("style").Set("color", "") return nil }) func main() { spans := js.Global.Get("document").Call("getElementById", "container").Call("querySelectorAll", "span") // access individual span length := spans.Get("length").Int() for i := 0; i < length; i++ { span := spans.Call("item", i) span.Set("onmouseover", onWordMouseOver) span.Set("onmouseout", onWordMouseOut) } }
func createFuncWithVisibleDone(fn func(func())) *js.Object { return js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} { return js.Global.Get("eval").Invoke("var a = function(cb){ return function(done){ return cb(done); }; }; a") }).Invoke().Invoke(fn) }
func main() { js.Global.Set("onmessage", js.MakeFunc(messageHandler)) }