Compare commits

...

21 Commits

Author SHA1 Message Date
Lea Anthony
2d5825d73d Speed up Wails Serve 2019-04-20 12:15:19 +10:00
Lea Anthony
c4a042cb1d Added Runtime.FileSystem 2019-04-20 12:13:55 +10:00
Lea Anthony
205f9476fa Change 'Headless' to 'Bridge' in logging 2019-04-20 12:12:30 +10:00
Lea Anthony
a1230fcbb6 Badges! 2019-04-11 08:43:50 +10:00
Lea Anthony
ba5c32a4a1 more linting fixes 2019-04-11 08:38:14 +10:00
Lea Anthony
58eee64326 more linting fixes 2019-04-11 08:26:57 +10:00
Lea Anthony
41d786a13c Add devtools to Vue config in template 2019-04-10 08:48:12 +10:00
Lea Anthony
a2b7906c89 more linting fixes 2019-04-10 08:46:49 +10:00
Lea Anthony
eeb6fa4677 linting fixes 2019-04-10 08:38:46 +10:00
Lea Anthony
6a36d75774 Use cropped logo 2019-04-08 19:33:44 +10:00
Lea Anthony
6078f3c780 Center subtitle 2019-04-08 19:29:17 +10:00
Lea Anthony
fc11197725 Fix image 2019-04-08 19:24:20 +10:00
Lea Anthony
8c40b99194 Update license and readme 2019-04-08 19:18:39 +10:00
Lea Anthony
de53fc6510 chore: remove unused files 2019-03-29 08:25:18 +11:00
Lea Anthony
d4f4feb429 fix: better input during setup 2019-03-29 08:25:09 +11:00
Lea Anthony
02973c49ff fix: version output of built app 2019-03-29 08:24:46 +11:00
Lea Anthony
01dce9f139 Merge pull request #68 from wailsapp/fix-packaging
fix: call package when -p flag provided
2019-03-20 08:12:48 +11:00
Lea Anthony
6c0906e87d fix: call package when -p flag provided 2019-03-20 08:11:46 +11:00
Lea Anthony
d7cfc4c71a Merge pull request #67 from wailsapp/fix-concurrent-websocket-writes
fix: use mutex to serialise websocket writes
2019-03-19 08:35:49 +11:00
Lea Anthony
fec3e7eac5 fix: use mutex to serialise websocket writes 2019-03-19 08:35:09 +11:00
Lea Anthony
9f5e2c7dd4 Merge pull request #66 from wailsapp/handle-javascript-nulls
fix: convert js nulls to Go zero values
2019-03-19 08:21:31 +11:00
28 changed files with 247 additions and 136 deletions

View File

@@ -6,7 +6,7 @@ before:
builds:
- env:
- CGO_ENABLED=0
main: ./cmd/wails/main.go
main: ./cmd/wails/main.go
archive:
replacements:
darwin: Darwin

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018 wailsapp
Copyright (c) 2018-Present Lea Anthony
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1 +1,84 @@
# Coming Soon
<p align="center" style="text-align: center">
<img src="https://github.com/wailsapp/docs/raw/master/.vuepress/public/media/logo_cropped.png" width="40%"><br/>
</p>
<p align="center">
A framework for building desktop applications using Go & Web Technologies.<br/><br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails"><img src="https://goreportcard.com/badge/github.com/wailsapp/wails"/></a>
<a href="http://godoc.org/github.com/wailsapp/wails"><img src="https://img.shields.io/badge/godoc-reference-blue.svg"/></a>
<a href="https://www.codefactor.io/repository/github/wailsapp/wails"><img src="https://www.codefactor.io/repository/github/wailsapp/wails/badge" alt="CodeFactor" /></a>
<a href="https://github.com/wailsapp/wails/issues"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" /></a>
</p>
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
## Features
- Use standard Go libraries/frameworks for the backend
- Use any frontend technology to build your UI
- Expose Go methods/functions to the frontend via a single bind command
- Uses native rendering engines - no embedded browser
- Shared events system
- Native file dialogs
- Powerful cli tool
- Multiplatform
## Installation
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are:
- Go 1.11 or above
- npm
### MacOS
Make sure you have the xcode command line tools installed. This can be done by running:
`xcode-select --install`
### Linux
#### Ubuntu 18.04
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`
Note: If you have successfully installed these dependencies on a different flavour of Linux, please consider submitting a PR.
### Windows
Windows requires gcc and related tooling. The recommended download is from [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download). Once this is installed, you are good to go.
## Installation
Installation is as simple as running the following command:
<pre style='color:white'>
go get -u github.com/wailsapp/wails/cmd/wails
</pre>
## Next Steps
It is recommended at this stage to read the comprehensive documentation at [https://wails.app](https://wails.app).
## FAQ
* Is this an alternative to Electron?
Depends on your requirements. It's designed to make it easy for Go programmers to make lightweight desktop applications or add a frontend to their existing applications. Whilst Wails does not currently offer hooks into native elements such as menus, this may change in the future.
* Who is this project aimed at?
Go programmers who want to bundle an HMTL frontend with their applications, without resorting to creating a server and opening a browser to view it.
## Shoulders of Giants
Without the following people, this project would never have existed:
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been immense. More patience than you can throw a stick at.
* [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses for the windowing.
Special Mentions:
* [Bill Kennedy](https://twitter.com/goinggodotnet) - Go guru, encourager and all-round nice guy, whose energy and inspiration powered me on when I had none left.
* [Mark Bates](https://github.com/markbates) - Creator of [Packr](https://github.com/gobuffalo/packr), inspiration for packing strategies.

View File

@@ -9,6 +9,7 @@ func (app *App) setupCli() *cmd.Cli {
// Create a new cli
result := cmd.NewCli(app.config.Title, "Debug build")
result.Version(cmd.Version)
// Setup cli to handle loglevel and headless flags
result.

View File

@@ -195,10 +195,7 @@ func (c *Command) Action(callback Action) *Command {
// PrintHelp - Output the help text for this command
func (c *Command) PrintHelp() {
c.log.PrintBanner()
versionString := c.AppVersion
if versionString != "" {
versionString = " " + versionString
}
commandTitle := c.CommandPath
if c.Shortdescription != "" {
commandTitle += " - " + c.Shortdescription

File diff suppressed because one or more lines are too long

View File

@@ -116,6 +116,14 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
}
packSpinner.Success()
// packageApp
if packageApp {
err = PackageApplication(projectOptions)
if err != nil {
return err
}
}
return nil
}

View File

@@ -12,7 +12,7 @@ const (
// Unknown is the catch-all distro
Unknown LinuxDistribution = 0
// Ubuntu distribution
Ubuntu LinuxDistribution = 1
Ubuntu LinuxDistribution = 1
)
// DistroInfo contains all the information relating to a linux distribution
@@ -67,13 +67,11 @@ func GetLinuxDistroInfo() *DistroInfo {
// DpkgInstalled uses dpkg to see if a package is installed
func DpkgInstalled(packageName string) (bool, error) {
result := false
program := NewProgramHelper()
dpkg := program.FindProgram("dpkg")
if dpkg == nil {
return false, fmt.Errorf("cannot check dependencies: dpkg not found")
}
_, _, exitCode, _ := dpkg.Run("-L", packageName)
result = exitCode == 0
return result, nil
return exitCode == 0, nil
}

View File

@@ -72,7 +72,7 @@ func (b *PackageHelper) Package(po *ProjectOptions) error {
case "darwin":
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
return fmt.Errorf("cannot bundle non-existant binary file '%s'. Please build with 'wails build' first", po.BinaryName)
return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
return b.packageOSX(po)
case "windows":
@@ -255,9 +255,5 @@ func (b *PackageHelper) packageIconOSX(resourceDir string) error {
}
defer dest.Close()
if err := icns.Encode(dest, srcImg); err != nil {
return err
}
return nil
return icns.Encode(dest, srcImg)
}

View File

@@ -56,7 +56,7 @@ func (p *Program) GetFullPathToBinary() (string, error) {
}
// Run will execute the program with the given parameters
// Returns stdout + stderr as strings and an error if one occured
// Returns stdout + stderr as strings and an error if one occurred
func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err error) {
command, err := p.GetFullPathToBinary()
if err != nil {

View File

@@ -1 +0,0 @@
package cmd

View File

@@ -81,21 +81,24 @@ func (s *SystemHelper) BackupConfig() (string, error) {
func (s *SystemHelper) setup() error {
systemConfig := make(map[string]string)
// Try to load current values - ignore errors
config, err := s.LoadConfig()
defaultName := ""
defaultEmail := ""
if config != nil {
defaultName = config.Name
defaultEmail = config.Email
config, _ := s.LoadConfig()
if config.Name != "" {
systemConfig["name"] = PromptRequired("What is your name", config.Name)
} else {
systemConfig["name"] = PromptRequired("What is your name")
}
if config.Email != "" {
systemConfig["email"] = PromptRequired("What is your email address", config.Email)
} else {
systemConfig["email"] = PromptRequired("What is your email address")
}
systemConfig := make(map[string]string)
systemConfig["name"] = PromptRequired("What is your name", defaultName)
systemConfig["email"] = PromptRequired("What is your email address", defaultEmail)
// Create the directory
err = s.fs.MkDirs(s.wailsSystemDir)
err := s.fs.MkDirs(s.wailsSystemDir)
if err != nil {
return err
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>my-vue-app-01</title>
</head>
<body>
<noscript>
<strong>We're sorry but my-vue-app-01 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -2,6 +2,7 @@ import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
Vue.config.devtools = true;
import Bridge from "./wailsbridge";

View File

@@ -18,11 +18,13 @@ func init() {
setupCommand.Action(func() error {
logger.PrintBanner();
logger.PrintBanner()
var err error
system := cmd.NewSystemHelper()
err := system.Initialise()
if err != nil {
err = system.Initialise()
if err == nil {
return err
}
@@ -39,13 +41,15 @@ Create your first project by running 'wails init'.`
// Check we have a cgo capable environment
logger.Yellow("Checking for prerequisites...")
errors, err := checkRequiredPrograms()
var requiredProgramErrors bool
requiredProgramErrors, err = checkRequiredPrograms()
if err != nil {
return err
}
// Linux has library deps
errors, err = checkLibraries()
var libraryErrors bool
libraryErrors, err = checkLibraries()
if err != nil {
return err
}
@@ -58,6 +62,8 @@ Create your first project by running 'wails init'.`
logger.White("")
// Check for errors
var errors = libraryErrors || requiredProgramErrors
if !errors {
logger.Yellow(successMessage)
}

View File

@@ -26,6 +26,9 @@ func init() {
initCmd.Action(func() error {
message := "Building Application"
if packageApp {
message = "Packaging Application"
}
if forceRebuild {
message += " (force rebuild)"
}
@@ -40,7 +43,7 @@ func init() {
fs := cmd.NewFSHelper()
err := projectOptions.LoadConfig(fs.Cwd())
if err != nil {
return err
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
}
// Validate config

View File

@@ -2,7 +2,6 @@ package main
import (
"fmt"
"os"
"github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd"
@@ -42,40 +41,6 @@ func init() {
return err
}
// Validate config
// Check if we have a frontend
err = cmd.ValidateFrontendConfig(projectOptions)
if err != nil {
return err
}
// Program checker
program := cmd.NewProgramHelper()
if projectOptions.FrontEnd != nil {
// npm
if !program.IsInstalled("npm") {
return fmt.Errorf("it appears npm is not installed. Please install and run again")
}
}
// Save project directory
projectDir := fs.Cwd()
// Install deps
if projectOptions.FrontEnd != nil {
err = cmd.InstallFrontendDeps(projectDir, projectOptions, forceRebuild, "serve")
if err != nil {
return err
}
}
// Move to project directory
err = os.Chdir(projectDir)
if err != nil {
return err
}
// Install dependencies
err = cmd.InstallGoDependencies()
if err != nil {

View File

@@ -1,10 +1,10 @@
package main
import (
"fmt"
"net/http"
"io/ioutil"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/leaanthony/spinner"
"github.com/wailsapp/wails/cmd"
@@ -54,7 +54,7 @@ func init() {
updateSpinner := spinner.NewSpinner()
updateSpinner.SetSpinSpeed(40)
updateSpinner.Start("Updating to : " + latestVersion)
err = cmd.NewProgramHelper().RunCommandArray([]string{"go","get","-u","github.com/wailsapp/wails/cmd/wails"})
err = cmd.NewProgramHelper().RunCommandArray([]string{"go", "get", "-u", "github.com/wailsapp/wails/cmd/wails"})
if err != nil {
updateSpinner.Error(err.Error())
return err

View File

@@ -87,7 +87,7 @@ func (e *eventManager) start(renderer Renderer) {
var wg sync.WaitGroup
wg.Add(1)
// Run main loop in seperate goroutine
// Run main loop in separate goroutine
go func() {
wg.Done()
e.log.Info("Listening")
@@ -126,7 +126,7 @@ func (e *eventManager) start(renderer Renderer) {
}
}
// Remove expired listners in place
// Remove expired listeners in place
if len(listenersToRemove) > 0 {
listeners := e.listeners[event.Name][:0]
for _, listener := range listeners {

2
go.mod
View File

@@ -9,7 +9,7 @@ require (
github.com/gorilla/websocket v1.4.0
github.com/jackmordaunt/icns v1.0.0
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/leaanthony/mewn v0.10.2
github.com/leaanthony/mewn v0.10.4
github.com/leaanthony/slicer v1.3.1
github.com/leaanthony/spinner v0.5.0
github.com/mattn/go-colorable v0.1.1 // indirect

4
go.sum
View File

@@ -21,8 +21,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/leaanthony/mewn v0.10.2 h1:Kzu1NqHHXriuMyGiHDMLk4aTzydZ3qbvz/qmoquGkvQ=
github.com/leaanthony/mewn v0.10.2/go.mod h1:i3ygCWW96qVQlGa8sjWnTM0IKAijoFvTwATDIZgK4k0=
github.com/leaanthony/mewn v0.10.4 h1:b16/E0r6CuXN8WYuDJIgwRocLQlmMjfBsooEJr6FdqY=
github.com/leaanthony/mewn v0.10.4/go.mod h1:i3ygCWW96qVQlGa8sjWnTM0IKAijoFvTwATDIZgK4k0=
github.com/leaanthony/slicer v1.3.1 h1:n2iIV2sxvL/3bpnmVY0vBjXf3yYFWcB6CYLVMrzJxRw=
github.com/leaanthony/slicer v1.3.1/go.mod h1:VMB/HGvr3uR3MRpFWHWAm0w+DHQLzPHYe2pKfpFlQIQ=
github.com/leaanthony/spinner v0.5.0 h1:HQykt/iTy7fmINEREtRbWrt+8j4MxC8dtvWBxEWM9oA=

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"strings"
"sync"
"github.com/dchest/htmlmin"
"github.com/gorilla/websocket"
@@ -45,6 +46,9 @@ type Headless struct {
initialisationJS []string
server *http.Server
theConnection *websocket.Conn
// Mutex for writing to the socket
lock sync.Mutex
}
// Initialise the Headless Renderer
@@ -53,7 +57,7 @@ func (h *Headless) Initialise(appConfig *AppConfig, ipcManager *ipcManager, even
h.appConfig = appConfig
h.eventManager = eventManager
ipcManager.bindRenderer(h)
h.log = newCustomLogger("Headless")
h.log = newCustomLogger("Bridge")
return nil
}
@@ -103,6 +107,10 @@ func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
}
func (h *Headless) sendMessage(conn *websocket.Conn, msg string) {
h.lock.Lock()
defer h.lock.Unlock()
if err := conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
h.log.Error(err.Error())
}
@@ -145,8 +153,8 @@ func (h *Headless) Run() error {
h.server = &http.Server{Addr: ":34115"}
http.HandleFunc("/bridge", h.wsBridgeHandler)
h.log.Info("Headless mode started.")
h.log.Info("The Wails bridge will connect automatically.")
h.log.Info("Bridge mode started.")
h.log.Info("The frontend will connect automatically.")
err := h.server.ListenAndServe()
if err != nil {

View File

@@ -122,7 +122,7 @@ func (w *webViewRenderer) evalJSSync(js string) error {
go func() {
exit := false
// We are done when we recieve the Callback ID
// We are done when we receive the Callback ID
w.log.Debug("SyncJS: sending with ID = " + ID)
w.eventManager.On(ID, func(...interface{}) {
w.log.Debug("SyncJS: Got callback ID = " + ID)

View File

@@ -2,17 +2,19 @@ package wails
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
type Runtime struct {
Events *RuntimeEvents
Log *RuntimeLog
Dialog *RuntimeDialog
Window *RuntimeWindow
Events *RuntimeEvents
Log *RuntimeLog
Dialog *RuntimeDialog
Window *RuntimeWindow
FileSystem *RuntimeFileSystem
}
func newRuntime(eventManager *eventManager, renderer Renderer) *Runtime {
return &Runtime{
Events: newRuntimeEvents(eventManager),
Log: newRuntimeLog(),
Dialog: newRuntimeDialog(renderer),
Window: newRuntimeWindow(renderer),
Events: newRuntimeEvents(eventManager),
Log: newRuntimeLog(),
Dialog: newRuntimeDialog(renderer),
Window: newRuntimeWindow(renderer),
FileSystem: newRuntimeFileSystem(),
}
}

16
runtime_filesystem.go Normal file
View File

@@ -0,0 +1,16 @@
package wails
import homedir "github.com/mitchellh/go-homedir"
// RuntimeFileSystem exposes file system utilities to the runtime
type RuntimeFileSystem struct {
}
func newRuntimeFileSystem() *RuntimeFileSystem {
return &RuntimeFileSystem{}
}
// HomeDir returns the user's home directory
func (r *RuntimeFileSystem) HomeDir() (string, error) {
return homedir.Dir()
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
(function(){window.wails=window.wails||{};window.backend={};function cryptoRandom(){var array=new Uint32Array(1);return window.crypto.getRandomValues(array)[0]}function basicRandom(){return Math.random()*9007199254740991}var randomFunc;if(window.crypto){randomFunc=cryptoRandom}else{randomFunc=basicRandom}function isValidIdentifier(name){try{new Function("var "+name);return true}catch(e){return false}}function addScript(js,callbackID){var script=document.createElement("script");script.text=js;document.body.appendChild(script);window.wails.events.emit(callbackID)}function injectCSS(css){var elem=document.createElement('style');elem.setAttribute('type','text/css');if(elem.styleSheet){elem.styleSheet.cssText=css}else{elem.appendChild(document.createTextNode(css))}var head=document.head||document.getElementsByTagName('head')[0];head.appendChild(elem)}var bindingsBasePath=window.backend;function addBindingPath(pathSections){var currentPath=bindingsBasePath;for(var sectionIndex in pathSections){var section=pathSections[sectionIndex];if(!isValidIdentifier(section)){var errMessage=section+" is not a valid javascript identifier.";var err=new Error(errMessage);return[null,err]}if(!currentPath[section]){currentPath[section]={}}currentPath=currentPath[section]}return[currentPath,null]}function newBinding(bindingName){var bindingSections=bindingName.split('.').splice(1);var callName=bindingSections.pop();var pathToBinding;var err;var bs=addBindingPath(bindingSections);var pathToBinding=bs[0];var err=bs[1];if(err!=null){return err}pathToBinding[callName]=function(){var timeout=0;function dynamic(){var args=[].slice.call(arguments);return call(bindingName,args,timeout)}dynamic.setTimeout=function(newTimeout){timeout=newTimeout};dynamic.getTimeout=function(){return timeout};return dynamic}()}var callbacks={};function call(bindingName,data,timeout){if(timeout==null||timeout==undefined){timeout=0}return new Promise(function(resolve,reject){var callbackID;do{callbackID=bindingName+"-"+randomFunc()}while(callbacks[callbackID]);if(timeout>0){var timeoutHandle=setTimeout(function(){reject(Error("Call to "+bindingName+" timed out. Request ID: "+callbackID))},timeout)}callbacks[callbackID]={timeoutHandle:timeoutHandle,reject:reject,resolve:resolve};try{var payloaddata=JSON.stringify(data);message={type:"call",callbackid:callbackID,payload:{bindingName:bindingName,data:payloaddata}};var payload=JSON.stringify(message);external.invoke(payload)}catch(e){console.error(e)}})}function callback(incomingMessage){var message;try{message=JSON.parse(incomingMessage)}catch(e){wails.log.debug("Invalid JSON passed to callback: "+e.message);wails.log.debug("Message: "+incomingMessage);return}callbackID=message.callbackid;callbackData=callbacks[callbackID];if(!callbackData){console.error("Callback '"+callbackID+"' not registed!!!");return}clearTimeout(callbackData.timeoutHandle);delete callbacks[callbackID];if(message.error){return callbackData.reject(message.error)}return callbackData.resolve(message.data)}var eventListeners={};function on(eventName,callback){eventListeners[eventName]=eventListeners[eventName]||[];eventListeners[eventName].push(callback)}function notify(eventName,data){if(eventListeners[eventName]){eventListeners[eventName].forEach(function(element){var parsedData=[];if(data){try{parsedData=JSON.parse(data)}catch(e){wails.log.error("Invalid JSON data sent to notify. Event name = "+eventName)}}element.apply(null,parsedData)})}}function emit(eventName){var data=JSON.stringify([].slice.apply(arguments).slice(1));message={type:"event",payload:{name:eventName,data:data}};external.invoke(JSON.stringify(message))}window.wails.events={emit:emit,on:on};function sendLogMessage(level,message){message={type:"log",payload:{level:level,message:message}};external.invoke(JSON.stringify(message))}function logDebug(message){sendLogMessage("debug",message)}function logInfo(message){sendLogMessage("info",message)}function logWarning(message){sendLogMessage("warning",message)}function logError(message){sendLogMessage("error",message)}function logFatal(message){sendLogMessage("fatal",message)}window.wails.log={debug:logDebug,info:logInfo,warning:logWarning,error:logError,fatal:logFatal};window.wails._={newBinding:newBinding,callback:callback,notify:notify,sendLogMessage:sendLogMessage,callbacks:callbacks,injectCSS:injectCSS,addScript:addScript};window.wails.events.emit("wails:loaded");})();
(function(){window.wails=window.wails||{};window.backend={};function cryptoRandom(){var array=new Uint32Array(1);return window.crypto.getRandomValues(array)[0]}
function basicRandom(){return Math.random()*9007199254740991}
var randomFunc;if(window.crypto){randomFunc=cryptoRandom}else{randomFunc=basicRandom}
function isValidIdentifier(name){try{new Function("var "+name);return!0}catch(e){return!1}}
function addScript(js,callbackID){var script=document.createElement("script");script.text=js;document.body.appendChild(script);window.wails.events.emit(callbackID)}
function injectCSS(css){var elem=document.createElement('style');elem.setAttribute('type','text/css');if(elem.styleSheet){elem.styleSheet.cssText=css}else{elem.appendChild(document.createTextNode(css))}
var head=document.head||document.getElementsByTagName('head')[0];head.appendChild(elem)}
var bindingsBasePath=window.backend;function addBindingPath(pathSections){var currentPath=bindingsBasePath;for(var sectionIndex in pathSections){var section=pathSections[sectionIndex];if(!isValidIdentifier(section)){var errMessage=section+" is not a valid javascript identifier.";var err=new Error(errMessage);return[null,err]}
if(!currentPath[section]){currentPath[section]={}}
currentPath=currentPath[section]}
return[currentPath,null]}
function newBinding(bindingName){var bindingSections=bindingName.split('.').splice(1);var callName=bindingSections.pop();var pathToBinding;var err;var bs=addBindingPath(bindingSections);var pathToBinding=bs[0];var err=bs[1];if(err!=null){return err}
pathToBinding[callName]=function(){var timeout=0;function dynamic(){var args=[].slice.call(arguments);return call(bindingName,args,timeout)}
dynamic.setTimeout=function(newTimeout){timeout=newTimeout}
dynamic.getTimeout=function(){return timeout}
return dynamic}()}
var callbacks={};function call(bindingName,data,timeout){if(timeout==null||timeout==undefined){timeout=0}
return new Promise(function(resolve,reject){var callbackID;do{callbackID=bindingName+"-"+randomFunc()}while(callbacks[callbackID]);if(timeout>0){var timeoutHandle=setTimeout(function(){reject(Error("Call to "+bindingName+" timed out. Request ID: "+callbackID))},timeout)}
callbacks[callbackID]={timeoutHandle:timeoutHandle,reject:reject,resolve:resolve}
try{var payloaddata=JSON.stringify(data);message={type:"call",callbackid:callbackID,payload:{bindingName:bindingName,data:payloaddata,}}
var payload=JSON.stringify(message);external.invoke(payload)}catch(e){console.error(e)}})}
function callback(incomingMessage){incomingMessage=decodeURIComponent(incomingMessage.replace(/\s+/g,'').replace(/[0-9a-f]{2}/g,'%$&'));var message;try{message=JSON.parse(incomingMessage)}catch(e){wails.log.debug("Invalid JSON passed to callback: "+e.message);wails.log.debug("Message: "+incomingMessage);return}
callbackID=message.callbackid;callbackData=callbacks[callbackID];if(!callbackData){console.error("Callback '"+callbackID+"' not registed!!!");return}
clearTimeout(callbackData.timeoutHandle);delete callbacks[callbackID];if(message.error){return callbackData.reject(message.error)}
return callbackData.resolve(message.data)}
var eventListeners={};function on(eventName,callback){eventListeners[eventName]=eventListeners[eventName]||[];eventListeners[eventName].push(callback)}
function notify(eventName,data){if(eventListeners[eventName]){eventListeners[eventName].forEach(function(element){var parsedData=[];if(data){try{parsedData=JSON.parse(data)}catch(e){wails.log.error("Invalid JSON data sent to notify. Event name = "+eventName)}}
element.apply(null,parsedData)})}}
function emit(eventName){var data=JSON.stringify([].slice.apply(arguments).slice(1));message={type:"event",payload:{name:eventName,data:data,}}
external.invoke(JSON.stringify(message))}
window.wails.events={emit:emit,on:on};function sendLogMessage(level,message){message={type:"log",payload:{level:level,message:message,}}
external.invoke(JSON.stringify(message))}
function logDebug(message){sendLogMessage("debug",message)}
function logInfo(message){sendLogMessage("info",message)}
function logWarning(message){sendLogMessage("warning",message)}
function logError(message){sendLogMessage("error",message)}
function logFatal(message){sendLogMessage("fatal",message)}
window.wails.log={debug:logDebug,info:logInfo,warning:logWarning,error:logError,fatal:logFatal,};window.wails._={newBinding:newBinding,callback:callback,notify:notify,sendLogMessage:sendLogMessage,callbacks:callbacks,injectCSS:injectCSS,addScript:addScript,}
window.wails.events.emit("wails:loaded")})()