├── .gitignore
├── spec
├── page1.html
├── support
│ └── jasmine.json
├── app-spec.go
├── webContents-spec.go
└── browserWindow-spec.go
├── scripts
├── test.go
└── test.js
├── emitter.go
├── .drone.yml
├── package.json
├── .travis.yml
├── app.go
├── README.md
├── browserWindow.go
├── webContents.go
└── electron.go
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | spec/*.js.map
3 | spec/*.js
4 | ./*.js
5 | ./*.js.map
6 | *.log
7 |
--------------------------------------------------------------------------------
/spec/page1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Page1
4 | 111
5 |
6 |
7 |
--------------------------------------------------------------------------------
/scripts/test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import _ "github.com/arvitaly/gopherjs-electron/spec"
4 |
5 | func main() {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/emitter.go:
--------------------------------------------------------------------------------
1 | package electron
2 |
3 | type EventEmitter interface {
4 | On(Event string, listener func(args ...interface{}))
5 | }
6 |
--------------------------------------------------------------------------------
/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "helpers": [
7 | "helpers/**/*.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | console.log("Start jasmine tests")
2 |
3 | var Jasmine = require('jasmine');
4 | var jasmine = new Jasmine();
5 |
6 | jasmine.loadConfigFile('spec/support/jasmine.json');
7 |
8 | jasmine.execute();
9 |
--------------------------------------------------------------------------------
/.drone.yml:
--------------------------------------------------------------------------------
1 | build:
2 | image: arvitaly/electron-go:latest
3 | commands:
4 | - go get
5 | - go build
6 | - go test
7 | - go get github.com/arvitaly/gopherjs-jasmine
8 | - npm install
9 | - export DISPLAY=':99.0'
10 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
11 | - npm test --
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gopherjs-electron",
3 | "version": "0.0.1",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "electron main.js",
8 | "test": "gopherjs build ./scripts/test.go -o ./spec/all-spec.js && electron scripts/test.js"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "jasmine": "^2.4.1"
14 | },
15 | "dependencies": {}
16 | }
17 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.5
4 | node_js:
5 | - "0.12.7"
6 | sudo: false
7 | addons:
8 | apt:
9 | packages:
10 | - xvfb
11 | install:
12 | - . $HOME/.nvm/nvm.sh
13 | - nvm install stable
14 | - nvm use stable
15 | - npm install
16 | - npm install electron-prebuilt -g
17 | - go get github.com/arvitaly/gopherjs-jasmine
18 | - go get -u github.com/gopherjs/gopherjs
19 | - export DISPLAY=':99.0'
20 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
21 | script:
22 | - npm test
23 |
--------------------------------------------------------------------------------
/app.go:
--------------------------------------------------------------------------------
1 | package electron
2 |
3 | import "github.com/gopherjs/gopherjs/js"
4 |
5 | type App interface {
6 | OnWillQuit(listener func(event *js.Object))
7 | OnReady(listener func())
8 | GetAppPath() string
9 | }
10 |
11 | type _App struct {
12 | *js.Object
13 | }
14 |
15 | func (a *_App) OnReady(listener func()) {
16 | a.Call("on", "ready", func() {
17 | listener()
18 | })
19 | }
20 | func (a *_App) OnWillQuit(listener func(event *js.Object)) {
21 | a.Call("on", "will-quit", func(event *js.Object) {
22 | listener(event)
23 | })
24 | }
25 |
26 | func (a *_App) GetAppPath() string {
27 | return a.Call("getAppPath").String()
28 | }
29 |
--------------------------------------------------------------------------------
/spec/app-spec.go:
--------------------------------------------------------------------------------
1 | package spec
2 |
3 | import "github.com/arvitaly/gopherjs-electron"
4 | import "github.com/gopherjs/gopherjs/js"
5 | import "github.com/arvitaly/gopherjs-jasmine"
6 |
7 | var _ = jasmine.Run(func() {
8 | jasmine.Describe("App", func() {
9 | var app = electron.GetApp()
10 | jasmine.ItAsync("OnReady", func(done func()) {
11 | app.OnReady(func() {
12 | done()
13 | })
14 | })
15 | jasmine.It("GetAppPath", func() {
16 | jasmine.Expect(app.GetAppPath() != "").ToBeTruthy()
17 | })
18 | jasmine.ItAsync("OnWillQuit", func(done func()) {
19 | var firstTime = true
20 | app.OnWillQuit(func(event *js.Object) {
21 | if firstTime {
22 | event.Call("preventDefault")
23 | }
24 | firstTime = false
25 | done()
26 | })
27 | var br = electron.NewBrowserWindow(nil)
28 | br.Close()
29 | })
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gopherjs-electron
2 |
3 | [](https://greenkeeper.io/)
4 | Atom/Electron (https://github.com/atom/electron/) desktop apps with Go. Package use https://github.com/gopherjs/gopherjs.
5 |
6 | [](https://travis-ci.org/arvitaly/gopherjs-electron)
7 |
8 | # Install
9 |
10 | Package require electron-prebuilt npm-module
11 |
12 | npm install -g electron-prebuilt
13 |
14 | go get github.com/arvitaly/gopherjs-electron
15 |
16 | # Usage
17 |
18 | Look source-code and tests)) //TODO
19 |
20 | # Test
21 |
22 | For tests used Jasmine (http://jasmine.github.io/) and adapter fo go https://github.com/arvitaly/gopherjs-jasmine
23 |
24 | go get github.com/arvitaly/gopherjs-jasmine
25 | npm install electron-prebuilt -g
26 | npm install
27 | npm test
28 |
29 | # Docker
30 |
31 | Also you can use docker-image for electron (includes NodeJS, Golang, gopherjs, electron-prebuilt). Example for run app in docker-container in file .drone.yml (config for drone.io CI)
32 |
33 | docker pull arvitaly/electron-go
34 |
--------------------------------------------------------------------------------
/spec/webContents-spec.go:
--------------------------------------------------------------------------------
1 | package spec
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/arvitaly/gopherjs-electron"
7 | )
8 | import jasmine "github.com/arvitaly/gopherjs-jasmine"
9 | import "github.com/gopherjs/gopherjs/js"
10 |
11 | var wsTests = func() bool {
12 | jasmine.Describe("WebContents", func() {
13 | jasmine.ItAsync("ExecuteJavascript", func(done func()) {
14 | var w = electron.NewBrowserWindow(nil)
15 | w.LoadURL("file:///"+js.Global.Get("process").Call("cwd").String()+"/spec/page1.html", nil)
16 | w.GetWebContents().OnWillNavigate(func(event *js.Object, url string) {
17 | jasmine.Expect(url).ToBe("file:///url2")
18 | w.Close()
19 | done()
20 | })
21 | w.GetWebContents().ExecuteJavaScript("location.href='file:///url2'", nil)
22 | })
23 | jasmine.It("UserAgent", func() {
24 | var w = electron.NewBrowserWindow(nil)
25 | w.GetWebContents().SetUserAgent("user1")
26 | jasmine.Expect(w.GetWebContents().GetUserAgent()).ToBe("user1")
27 | w.Close()
28 | })
29 | jasmine.It("OnCrashed", func() {
30 | var w = electron.NewBrowserWindow(&map[string]interface{}{
31 | "webSecurity": false,
32 | })
33 | var crashed = make(chan bool)
34 | w.GetWebContents().OnCrashed(func() {
35 | go func() {
36 | crashed <- true
37 | }()
38 | })
39 | w.LoadURL("chrome://crash/", nil)
40 | <-crashed
41 | w.Close()
42 | })
43 | jasmine.AfterAllAsync(func(done func()) {
44 | time.AfterFunc(time.Millisecond*10, done)
45 | })
46 | })
47 | return true
48 | }()
49 |
--------------------------------------------------------------------------------
/browserWindow.go:
--------------------------------------------------------------------------------
1 | package electron
2 |
3 | import "github.com/gopherjs/gopherjs/js"
4 |
5 | type BrowserWindow interface {
6 | GetWebContents() WebContents
7 | LoadURL(url string, opts *map[string]interface{})
8 | GetTitle() string
9 | Close()
10 | Destroy()
11 |
12 | //Emitted when the window is going to be closed. It's emitted before the beforeunload and unload event of the DOM. Calling event.preventDefault() will cancel the close.
13 | OnClose(listener func(event *js.Object))
14 | //Emitted when the window is closed. After you have received this event you should remove the reference to the window and avoid using it anymore.
15 | OnClosed(listener func())
16 | }
17 | type _BrowserWindow struct {
18 | *js.Object
19 | WebContents WebContents
20 | }
21 |
22 | func (w *_BrowserWindow) GetWebContents() WebContents {
23 | return w.WebContents
24 | }
25 | func (w *_BrowserWindow) LoadURL(url string, opts *map[string]interface{}) {
26 | w.Call("loadURL", url, opts)
27 | }
28 |
29 | func (w *_BrowserWindow) GetTitle() string {
30 | return w.Call("getTitle").String()
31 | }
32 | func (w *_BrowserWindow) Destroy() {
33 | w.Call("destroy")
34 | }
35 | func (w *_BrowserWindow) Close() {
36 | w.Call("close")
37 | }
38 | func (w *_BrowserWindow) OnClose(listener func(event *js.Object)) {
39 | w.Call("on", "close", func(event *js.Object) {
40 | listener(event)
41 | })
42 | }
43 | func (w *_BrowserWindow) OnClosed(listener func()) {
44 | w.Call("on", "closed", func() {
45 | listener()
46 | })
47 | }
48 | func (w *_BrowserWindow) HookWindowMessage(message int, callback func(result ...interface{})) {
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/spec/browserWindow-spec.go:
--------------------------------------------------------------------------------
1 | package spec
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/arvitaly/gopherjs-electron"
7 | jasmine "github.com/arvitaly/gopherjs-jasmine"
8 | "github.com/gopherjs/gopherjs/js"
9 | )
10 |
11 | var bwTest = func() bool {
12 | jasmine.Describe("BrowserWindow", func() {
13 | var w electron.BrowserWindow
14 | jasmine.It("New", func() {
15 | w = electron.NewBrowserWindow(&map[string]interface{}{
16 | "title": "title1",
17 | })
18 | jasmine.Expect(w.GetTitle() == "title1").ToBeTruthy()
19 | })
20 | jasmine.ItAsync("LoadUrl", func(done func()) {
21 |
22 | w = electron.NewBrowserWindow(&map[string]interface{}{
23 | "title": "title2",
24 | })
25 | w.LoadURL("file:///"+js.Global.Get("process").Call("cwd").String()+"/spec/page1.html", nil)
26 | w.GetWebContents().OnDidStopLoading(func() {
27 | jasmine.Expect(w.GetTitle()).ToBe("Page1")
28 | done()
29 | })
30 |
31 | })
32 | jasmine.It("OnClose and OnClosed", func() {
33 | var w = electron.NewBrowserWindow(nil)
34 | var closec = make(chan bool)
35 | var first = true
36 | w.OnClose(func(event *js.Object) {
37 | if first {
38 | event.Call("preventDefault")
39 | }
40 | first = false
41 | go func() {
42 | closec <- true
43 | }()
44 |
45 | })
46 | w.Close()
47 | <-closec
48 | var closed = make(chan bool)
49 | w.OnClosed(func() {
50 | go func() {
51 | closed <- true
52 | }()
53 | })
54 | w.Close()
55 |
56 | <-closec
57 | <-closed
58 | })
59 | jasmine.AfterAllAsync(func(done func()) {
60 | time.AfterFunc(time.Millisecond*100, func() {
61 | w.Close()
62 | done()
63 | })
64 | })
65 | })
66 | return true
67 | }()
68 |
--------------------------------------------------------------------------------
/webContents.go:
--------------------------------------------------------------------------------
1 | package electron
2 |
3 | import "github.com/gopherjs/gopherjs/js"
4 |
5 | type WebContents interface {
6 | SetUserAgent(userAgent string)
7 | ExecuteJavaScript(code string, UserGesture *bool)
8 | GetUserAgent() string
9 | /*Events*/
10 | //Corresponds to the points in time when the spinner of the tab stopped spinning.
11 | OnDidStopLoading(listener func())
12 | OnDidFailLoad(listener func(event *js.Object, errorCode int, errorDescription string, validatedURL string))
13 | OnWillNavigate(listener func(event *js.Object, url string))
14 |
15 | //Emitted when the renderer process has crashed.
16 | OnCrashed(listener func())
17 | }
18 |
19 | type _WebContents struct {
20 | *js.Object
21 | EventEmitter
22 | }
23 |
24 | func (w *_WebContents) GetUserAgent() string {
25 | return w.Call("getUserAgent").String()
26 | }
27 |
28 | func (w *_WebContents) SetUserAgent(userAgent string) {
29 | w.Call("setUserAgent", userAgent)
30 | }
31 |
32 | // HttpReferrer string //A HTTP Referrer url.
33 | // UserAgent string //A user agent originating the request.
34 | // ExtraHeaders string //Extra headers separated by "\n"
35 | func (w *_WebContents) LoadURL(url string, opts *map[string]interface{}) {
36 | w.Call("loadURL", url, opts)
37 | }
38 | func (w *_WebContents) ExecuteJavaScript(code string, userGesture *bool) {
39 | w.Call("executeJavaScript", code, userGesture)
40 | }
41 | func (w *_WebContents) OnDidFailLoad(listener func(event *js.Object, errorCode int, errorDescription string, validatedURL string)) {
42 | w.Call("on", "did-fail-load", func(event *js.Object, errorCode *js.Object, errorDescription *js.Object, validatedURL *js.Object) {
43 | listener(event, errorCode.Int(), errorDescription.String(), validatedURL.String())
44 | })
45 | }
46 | func (w *_WebContents) OnWillNavigate(listener func(event *js.Object, url string)) {
47 | w.Call("on", "will-navigate", func(event *js.Object, url *js.Object) {
48 | listener(event, url.String())
49 | })
50 | }
51 |
52 | func (w *_WebContents) OnDidStopLoading(listener func()) {
53 | w.Call("on", "did-stop-loading", func() {
54 | go func() {
55 | listener()
56 | }()
57 | })
58 | }
59 |
60 | //TODO add test?
61 | func (w *_WebContents) OnCrashed(listener func()) {
62 | w.Call("on", "crashed", listener)
63 | }
64 |
--------------------------------------------------------------------------------
/electron.go:
--------------------------------------------------------------------------------
1 | package electron
2 |
3 | import "github.com/gopherjs/gopherjs/js"
4 |
5 | var jsElectron = js.Global.Get("require").Invoke("electron")
6 |
7 | func GetApp() App {
8 | return &_App{jsElectron.Get("app")}
9 | }
10 |
11 | //It creates a new BrowserWindow with native properties as set by the options
12 | //type BrowserWindowOptions struct {
13 | // Width int //Window's width in pixels. Default is 800
14 | // Height int //Window's height in pixels. Default is 600
15 | // X int //Window's left offset from screen. Default is to center the window.
16 | // Y int //Window's top offset from screen. Default is to center the window.
17 | // UseContentSize bool //The width and height would be used as web page's size, which means the actual window's size will include window frame's size and be slightly larger. Default is false.
18 | // Center bool //Show window in the center of the screen.
19 | // MinWidth int //Window's minimum width. Default is 0.
20 | // MinHeight int //Window's minimum height. Default is 0.
21 | // MaxWidth int //Window's maximum width. Default is no limit.
22 | // MaxHeight int //Window's maximum height. Default is no limit.
23 | // Resizable bool //Whether window is resizable. Default is true.
24 | // AlwaysOnTop bool //Whether the window should always stay on top of other windows. Default is false.
25 | // Fullscreen bool //Whether the window should show in fullscreen. When set to false the fullscreen button will be hidden or disabled on OS X. Default is false.
26 | // SkipTaskbar bool //Whether to show the window in taskbar. Default is false.
27 | // Kiosk bool //The kiosk mode. Default is false.
28 | // Title string //Default window title. Default is "Electron".
29 | // Icon interface{} //The window icon, when omitted on Windows the executable's icon would be used as window icon.
30 | // Show bool //Whether window should be shown when created. Default is true.
31 | // Frame bool //Specify false to create a Frameless Window. Default is true
32 | // AcceptFirstMouse bool
33 | // DisableAutoHideCursor bool
34 | // AutoHideMenuBar bool
35 | // EnableLargerThanScreen bool
36 | // BackgroundColor string `js:"backgroundColor"`
37 | // DarkTheme bool
38 | // Transparent bool
39 | // Type string
40 | // TitleBarStyle string
41 | // WebPreferences interface{}
42 | // NodeIntegration bool
43 | //}
44 | func NewBrowserWindow(opts *map[string]interface{}) BrowserWindow {
45 | var bw = jsElectron.Get("BrowserWindow").New(opts)
46 | return &_BrowserWindow{Object: bw, WebContents: &_WebContents{Object: bw.Get("webContents")}}
47 | }
48 |
--------------------------------------------------------------------------------